From fed1c0bbe10c43425bae1dd46c16775c1714e1e3 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 22 Apr 2020 16:22:34 -0700 Subject: [PATCH] s8 col mux array --- .../modules/single_level_column_mux_array.py | 115 +++++++++--------- compiler/pgates/single_level_column_mux.py | 45 ++++--- 2 files changed, 85 insertions(+), 75 deletions(-) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 324b7415..581e20b0 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -5,17 +5,15 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from math import log import design -import contact -from tech import drc import debug -import math +from tech import layer from vector import vector from sram_factory import factory from globals import OPTS import logical_effort + class single_level_column_mux_array(design.design): """ Dynamically generated column mux array. @@ -26,13 +24,20 @@ class single_level_column_mux_array(design.design): design.design.__init__(self, 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)) - + self.columns = columns self.word_size = word_size self.words_per_row = int(self.columns / self.word_size) self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br - + + if "li" in layer: + self.col_mux_stack = self.li_stack + self.col_mux_stack_pitch = self.li_pitch + else: + self.col_mux_stack = self.m1_stack + self.col_mux_stack_pitch = self.m1_pitch + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -49,20 +54,20 @@ class single_level_column_mux_array(design.design): self.add_modules() self.add_pins() self.create_array() - + def create_layout(self): self.setup_layout_constants() self.place_array() self.add_routing() # Find the highest shapes to determine height before adding well highest = self.find_highest_coords() - self.height = highest.y + self.height = highest.y self.add_layout_pins() - self.add_enclosure(self.mux_inst, "pwell") - + if "pwell" in layer: + self.add_enclosure(self.mux_inst, "pwell") self.add_boundary() self.DRC_LVS() - + def add_pins(self): for i in range(self.columns): self.add_pin("bl_{}".format(i)) @@ -74,23 +79,19 @@ class single_level_column_mux_array(design.design): self.add_pin("br_out_{}".format(i)) self.add_pin("gnd") - def add_modules(self): self.mux = factory.create(module_type="single_level_column_mux", bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br) self.add_mod(self.mux) - def setup_layout_constants(self): - self.column_addr_size = num_of_inputs = int(self.words_per_row / 2) + self.column_addr_size = int(self.words_per_row / 2) self.width = self.columns * self.mux.width # one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br # one extra route pitch is to space from the sense amp - self.route_height = (self.words_per_row + 3)*self.m1_pitch - + self.route_height = (self.words_per_row + 3) * self.col_mux_stack_pitch - def create_array(self): self.mux_inst = [] # For every column, add a pass gate @@ -98,11 +99,11 @@ class single_level_column_mux_array(design.design): name = "XMUX{0}".format(col_num) self.mux_inst.append(self.add_inst(name=name, mod=self.mux)) - + self.connect_inst(["bl_{}".format(col_num), "br_{}".format(col_num), - "bl_out_{}".format(int(col_num/self.words_per_row)), - "br_out_{}".format(int(col_num/self.words_per_row)), + "bl_out_{}".format(int(col_num / self.words_per_row)), + "br_out_{}".format(int(col_num / self.words_per_row)), "sel_{}".format(col_num % self.words_per_row), "gnd"]) @@ -117,32 +118,31 @@ class single_level_column_mux_array(design.design): else: mirror = "" - name = "XMUX{0}".format(col_num) offset = vector(xoffset, self.route_height) self.mux_inst[col_num].place(offset=offset, mirror=mirror) - def add_layout_pins(self): """ Add the pins after we determine the height. """ # For every column, add a pass gate for col_num in range(self.columns): mux_inst = self.mux_inst[col_num] - offset = mux_inst.get_pin("bl").ll() + bl_pin = mux_inst.get_pin("bl") + offset = bl_pin.ll() self.add_layout_pin(text="bl_{}".format(col_num), - layer="m2", + layer=bl_pin.layer, offset=offset, - height=self.height-offset.y) + height=self.height - offset.y) - offset = mux_inst.get_pin("br").ll() + br_pin = mux_inst.get_pin("br") + offset = br_pin.ll() self.add_layout_pin(text="br_{}".format(col_num), - layer="m2", + layer=br_pin.layer, offset=offset, - height=self.height-offset.y) + height=self.height - offset.y) for inst in self.mux_inst: self.copy_layout_pin(inst, "gnd") - def add_routing(self): self.add_horizontal_input_rail() self.add_vertical_poly_rail() @@ -151,15 +151,15 @@ class single_level_column_mux_array(design.design): def add_horizontal_input_rail(self): """ Create address input rails on M1 below the mux transistors """ for j in range(self.words_per_row): - offset = vector(0, self.route_height + (j-self.words_per_row)*self.m1_pitch) + offset = vector(0, self.route_height + (j - self.words_per_row) * self.col_mux_stack_pitch) #edit self.add_layout_pin(text="sel_{}".format(j), - layer="m1", + layer=self.col_mux_stack[0], offset=offset, width=self.mux.width * self.columns) def add_vertical_poly_rail(self): """ Connect the poly to the address rails """ - + # Offset to the first transistor gate in the pass gate for col in range(self.columns): # which select bit should this column connect to depends on the position in the word @@ -167,11 +167,12 @@ class single_level_column_mux_array(design.design): # Add the column x offset to find the right select bit gate_offset = self.mux_inst[col].get_pin("sel").bc() # height to connect the gate to the correct horizontal row - sel_height = self.get_pin("sel_{}".format(sel_index)).by() + # sel_height = self.get_pin("sel_{}".format(sel_index)).by() # use the y offset from the sel pin and the x offset from the gate - offset = vector(gate_offset.x,self.get_pin("sel_{}".format(sel_index)).cy()) + offset = vector(gate_offset.x, + self.get_pin("sel_{}".format(sel_index)).cy()) # Add the poly contact with a shift to account for the rotation - self.add_via_center(layers=("m1", "contact", "poly"), + self.add_via_center(layers=self.poly_stack, offset=offset) self.add_path("poly", [offset, gate_offset]) @@ -182,11 +183,11 @@ class single_level_column_mux_array(design.design): bl_offset = self.mux_inst[j].get_pin("bl_out").bc() br_offset = self.mux_inst[j].get_pin("br_out").bc() - bl_out_offset = bl_offset - vector(0,(self.words_per_row+1)*self.m1_pitch) - br_out_offset = br_offset - vector(0,(self.words_per_row+2)*self.m1_pitch) + bl_out_offset = bl_offset - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch) + br_out_offset = br_offset - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch) - bl_out_offset_end = bl_out_offset + vector(0,self.route_height) - br_out_offset_end = br_out_offset + vector(0,self.route_height) + bl_out_offset_end = bl_out_offset + vector(0, self.route_height) + br_out_offset_end = br_out_offset + vector(0, self.route_height) if cell_properties.bitcell.mirror.y and j % 2: tmp_bl_out_end = br_out_offset_end @@ -208,43 +209,41 @@ class single_level_column_mux_array(design.design): else: dist = 0 - self.add_path("m1", [bl_out_offset, bl_out_offset+vector(width+dist,0)]) - self.add_path("m1", [br_out_offset, br_out_offset+vector(width-dist,0)]) + self.add_path(self.col_mux_stack[0], [bl_out_offset, bl_out_offset + vector(width + dist, 0)]) + self.add_path(self.col_mux_stack[0], [br_out_offset, br_out_offset + vector(width - dist, 0)]) # Extend the bitline output rails and gnd downward on the first bit of each n-way mux - self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j/self.words_per_row)), - layer="m2", + self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)), + layer=self.col_mux_stack[2], start=bl_out_offset, end=tmp_bl_out_end) - self.add_layout_pin_segment_center(text="br_out_{}".format(int(j/self.words_per_row)), - layer="m2", + self.add_layout_pin_segment_center(text="br_out_{}".format(int(j / self.words_per_row)), + layer=self.col_mux_stack[2], start=br_out_offset, end=tmp_br_out_end) - - # This via is on the right of the wire - self.add_via_center(layers=self.m1_stack, + # This via is on the right of the wire + self.add_via_center(layers=self.col_mux_stack, offset=bl_out_offset) # This via is on the left of the wire - self.add_via_center(layers=self.m1_stack, + self.add_via_center(layers=self.col_mux_stack, offset=br_out_offset) else: - - self.add_path("m2", [ bl_out_offset, tmp_bl_out_end]) - self.add_path("m2", [ br_out_offset, tmp_br_out_end]) - + self.add_path(self.col_mux_stack[2], [bl_out_offset, bl_offset]) + self.add_path(self.col_mux_stack[2], [br_out_offset, br_offset]) + # This via is on the right of the wire - self.add_via_center(layers=self.m1_stack, + self.add_via_center(layers=self.col_mux_stack, offset=bl_out_offset) - # This via is on the left of the wire - self.add_via_center(layers=self.m1_stack, + # This via is on the left of the wire + self.add_via_center(layers=self.col_mux_stack, offset=br_out_offset) def get_drain_cin(self): """Get the relative capacitance of the drain of the NMOS pass TX""" from tech import parameter - #Bitcell drain load being used to estimate mux NMOS drain load + # Bitcell drain load being used to estimate mux NMOS drain load drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) - return drain_load + return drain_load diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 288894aa..9e38287f 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -121,13 +121,25 @@ class single_level_column_mux(pgate.pgate): # height is the distance between the nmos' diffusions, which depends on max(self.active_space,self.poly_space) offset = self.nmos_lower.get_pin("G").ul() - vector(0,self.poly_extend_active) height = self.nmos_upper.get_pin("G").by() + self.poly_extend_active - offset.y - self.add_layout_pin(text="sel", - layer="poly", + self.add_rect(layer="poly", offset=offset, height=height) + # Add the sel pin to the bottom of the mux + self.add_layout_pin(text="sel", + layer="poly", + offset=self.nmos_lower.get_pin("G").ll(), + height=self.poly_extend_active) + def connect_bitlines(self): """ Connect the bitlines to the mux transistors """ + + # If li exists, use li and m1 for the mux, otherwise use m1 and m2 + if "li" in layer: + self.col_mux_stack = self.li_stack + else: + self.col_mux_stack = self.m1_stack + # These are on metal2 bl_pin = self.get_pin("bl") br_pin = self.get_pin("br") @@ -140,29 +152,24 @@ class single_level_column_mux(pgate.pgate): nmos_upper_s_pin = self.nmos_upper.get_pin("S") nmos_upper_d_pin = self.nmos_upper.get_pin("D") - # If li exists, use li and m1 for the mux, otherwise use m1 and m2 - if "li" in layer: - col_mux_stack = self.li_stack - else: - col_mux_stack = self.m1_stack - # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D - self.add_via_center(layers=col_mux_stack, + self.add_via_center(layers=self.col_mux_stack, offset=bl_pin.bc(), directions=("V", "V")) - self.add_via_center(layers=col_mux_stack, + self.add_via_center(layers=self.col_mux_stack, offset=br_out_pin.uc(), directions=("V", "V")) - self.add_via_center(layers=col_mux_stack, + self.add_via_center(layers=self.col_mux_stack, offset=nmos_upper_s_pin.center(), directions=("V", "V")) - self.add_via_center(layers=col_mux_stack, + self.add_via_center(layers=self.col_mux_stack, offset=nmos_lower_d_pin.center(), directions=("V", "V")) # Add diffusion contacts # These were previously omitted with the options: add_source_contact=False, add_drain_contact=False - # They are added now and not previously due to a s8 tech special case in which the contacts intersected the mux intraconnect + # They are added now and not previously so that they do not include m1 (which is usually included by default) + # This is only a concern when the local interconnect (li) layer is being used self.add_via_center(layers=self.active_stack, offset=nmos_upper_d_pin.center(), directions=("V", "V"), @@ -186,7 +193,7 @@ class single_level_column_mux(pgate.pgate): # bl -> nmos_upper/D on metal1 # bl_out -> nmos_upper/S on metal2 - self.add_path(col_mux_stack[0], + self.add_path(self.col_mux_stack[0], [bl_pin.ll(), vector(nmos_upper_d_pin.cx(), bl_pin.by()), nmos_upper_d_pin.center()]) # halfway up, move over @@ -194,12 +201,12 @@ class single_level_column_mux(pgate.pgate): + nmos_upper_s_pin.bc().scale(0, 0.4) mid2 = bl_out_pin.uc().scale(0, 0.4) \ + nmos_upper_s_pin.bc().scale(1, 0.4) - self.add_path(col_mux_stack[2], + self.add_path(self.col_mux_stack[2], [bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.center()]) # br -> nmos_lower/D on metal2 # br_out -> nmos_lower/S on metal1 - self.add_path(col_mux_stack[0], + self.add_path(self.col_mux_stack[0], [br_out_pin.uc(), vector(nmos_lower_s_pin.cx(), br_out_pin.uy()), nmos_lower_s_pin.center()]) @@ -208,7 +215,7 @@ class single_level_column_mux(pgate.pgate): + nmos_lower_d_pin.uc().scale(0,0.5) mid2 = br_pin.bc().scale(0,0.5) \ + nmos_lower_d_pin.uc().scale(1,0.5) - self.add_path(col_mux_stack[2], + self.add_path(self.col_mux_stack[2], [br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()]) def add_wells(self): @@ -225,6 +232,10 @@ class single_level_column_mux(pgate.pgate): implant_type="p", well_type="p") + # If there is a li layer, include it in the power stack + self.add_via_center(layers=self.col_mux_stack, + offset=active_pos) + # Add the M1->..->power_grid_layer stack self.add_power_pin(name = "gnd", loc = active_pos,