diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 7b73a684..88248c1f 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -544,6 +544,109 @@ class layout(lef.lef): width=xmax-xmin, height=ymax-ymin) + def add_power_ring(self, bbox): + """ + Create vdd and gnd power rings around an area of the bounding box argument. Must + have a supply_rail_width and supply_rail_pitch defined as a member variable. + Defines local variables of the left/right/top/bottom vdd/gnd center offsets + for use in other modules.. + """ + + [ll, ur] = bbox + + supply_rail_spacing = self.supply_rail_pitch - self.supply_rail_width + height = (ur.y-ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing + width = (ur.x-ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing + + + # LEFT vertical rails + offset = ll + vector(-2*self.supply_rail_pitch, -2*self.supply_rail_pitch) + left_gnd_pin=self.add_layout_pin(text="gnd", + layer="metal2", + offset=offset, + width=self.supply_rail_width, + height=height) + + + offset = ll + vector(-1*self.supply_rail_pitch, -1*self.supply_rail_pitch) + left_vdd_pin=self.add_layout_pin(text="vdd", + layer="metal2", + offset=offset, + width=self.supply_rail_width, + height=height) + + # RIGHT vertical rails + offset = vector(ur.x,ll.y) + vector(0,-2*self.supply_rail_pitch) + right_gnd_pin = self.add_layout_pin(text="gnd", + layer="metal2", + offset=offset, + width=self.supply_rail_width, + height=height) + + offset = vector(ur.x,ll.y) + vector(self.supply_rail_pitch,-1*self.supply_rail_pitch) + right_vdd_pin=self.add_layout_pin(text="vdd", + layer="metal2", + offset=offset, + width=self.supply_rail_width, + height=height) + + # BOTTOM horizontal rails + offset = ll + vector(-2*self.supply_rail_pitch, -2*self.supply_rail_pitch) + bottom_gnd_pin=self.add_layout_pin(text="gnd", + layer="metal1", + offset=offset, + width=width, + height=self.supply_rail_width) + + offset = ll + vector(-1*self.supply_rail_pitch, -1*self.supply_rail_pitch) + bottom_vdd_pin=self.add_layout_pin(text="vdd", + layer="metal1", + offset=offset, + width=width, + height=self.supply_rail_width) + + # TOP horizontal rails + offset = vector(ll.x, ur.y) + vector(-2*self.supply_rail_pitch,0) + top_gnd_pin=self.add_layout_pin(text="gnd", + layer="metal1", + offset=offset, + width=width, + height=self.supply_rail_width) + + offset = vector(ll.x, ur.y) + vector(-1*self.supply_rail_pitch, self.supply_rail_pitch) + top_vdd_pin=self.add_layout_pin(text="vdd", + layer="metal1", + offset=offset, + width=width, + height=self.supply_rail_width) + + # Remember these for connecting things in the design + self.left_gnd_x_center = left_gnd_pin.cx() + self.left_vdd_x_center = left_vdd_pin.cx() + self.right_gnd_x_center = right_gnd_pin.cx() + self.right_vdd_x_center = right_vdd_pin.cx() + + self.bottom_gnd_y_center = bottom_gnd_pin.cy() + self.bottom_vdd_y_center = bottom_vdd_pin.cy() + self.top_gnd_y_center = top_gnd_pin.cy() + self.top_vdd_y_center = top_vdd_pin.cy() + + via_points = [vector(self.left_gnd_x_center, self.bottom_gnd_y_center), + vector(self.left_gnd_x_center, self.top_gnd_y_center), + vector(self.right_gnd_x_center, self.bottom_gnd_y_center), + vector(self.right_gnd_x_center, self.top_gnd_y_center), + vector(self.left_vdd_x_center, self.bottom_vdd_y_center), + vector(self.left_vdd_x_center, self.top_vdd_y_center), + vector(self.right_vdd_x_center, self.bottom_vdd_y_center), + vector(self.right_vdd_x_center, self.top_vdd_y_center)] + + for pt in via_points: + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=pt, + size = (3,3)) + + + def pdf_write(self, pdf_name): # NOTE: Currently does not work (Needs further research) #self.pdf_name = self.name + ".pdf" diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 9b25a344..643df1da 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -182,10 +182,12 @@ class pin_layout: width=self.width(), height=self.height(), center=False) + # Add the tet in the middle of the pin. + # This fixes some pin label offsetting when GDS gets imported into Magic. newLayout.addText(text=self.name, layerNumber=layer[self.layer], purposeNumber=0, - offsetInMicrons=self.ll(), + offsetInMicrons=self.center(), magnification=GDS["zoom"], rotate=None) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 6b77589e..8eb28893 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -9,12 +9,15 @@ from pinv import pinv from pnand2 import pnand2 from pnor2 import pnor2 from vector import vector +from pinvbuf import pinvbuf + from globals import OPTS class bank(design.design): """ - Dynamically generated a single Bank including bitcell array, - hierarchical_decoder, precharge, column_mux, write driver and sense amplifiers. + Dynamically generated a single bank including bitcell array, + hierarchical_decoder, precharge, (optional column_mux and column decoder), + write driver and sense amplifiers. """ def __init__(self, word_size, num_words, words_per_row, num_banks=1, name=""): @@ -40,7 +43,8 @@ class bank(design.design): self.num_banks = num_banks # The local control signals are gated when we have bank select logic, - # so this prefix will be added to all of the input signals. + # so this prefix will be added to all of the input signals to create + # the internal gated signals. if self.num_banks>1: self.prefix="gated_" else: @@ -51,13 +55,14 @@ class bank(design.design): self.create_modules() self.add_modules() self.setup_layout_constraints() - self.route_power_ring(self.core_bbox) - if self.num_banks > 1: - self.add_bank_select() + self.add_power_ring(self.core_bbox) + + # FIXME: Move this to the add modules function + self.add_bank_select() self.route_layout() - + # Can remove the following, but it helps for debug! self.add_lvs_correspondence_points() @@ -71,7 +76,7 @@ class bank(design.design): for i in range(self.word_size): self.add_pin("DATA[{0}]".format(i)) for i in range(self.addr_size): - self.add_pin("ADDR[{0}]".format(i)) + self.add_pin("A[{0}]".format(i)) # For more than one bank, we have a bank select and name # the signals gated_*. @@ -83,14 +88,14 @@ class bank(design.design): def route_layout(self): """ Create routing amoung the modules """ - self.create_central_bus() + self.route_central_bus() self.route_precharge_to_bitcell_array() self.route_sense_amp_to_trigate() self.route_tri_gate_out() self.route_wordline_driver() self.route_row_decoder() + self.route_address() self.route_column_address_lines() - self.route_msf_address() self.route_control_lines() self.add_control_pins() if self.num_banks > 1: @@ -101,9 +106,12 @@ class bank(design.design): def add_modules(self): """ Add modules. The order should not matter! """ + + # Above the bitcell array self.add_bitcell_array() self.add_precharge_array() + # Below the bitcell array if self.col_addr_size > 0: # The m2 width is because the 6T cell may have vias on the boundary edge for # overlapping when making the array @@ -111,16 +119,17 @@ class bank(design.design): self.add_column_mux_array() else: self.column_mux_height = 0 - if self.col_addr_size > 1: # size 1 is from addr FF - self.add_column_decoder() - self.add_sense_amp_array() self.add_write_driver_array() self.add_msf_data_in() self.add_tri_gate_array() + + # To the left of the bitcell array self.add_row_decoder() self.add_wordline_driver() - self.add_msf_address() + self.add_column_decoder() + + def compute_sizes(self): """ Computes the required sizes to create the bank """ @@ -143,28 +152,29 @@ class bank(design.design): self.num_control_lines = 6 # The order of the control signals on the control bus: self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_bar", "w_en", "s_en"] - # These will be outputs of the gaters if this is multibank + # These will be outputs of the gaters if this is multibank, if not, normal signals. if self.num_banks > 1: self.control_signals = ["gated_"+str for str in self.input_control_signals] else: self.control_signals = self.input_control_signals - # The central bus is the column address (both polarities), row address + # The central bus is the column address (one hot) and row address (binary) if self.col_addr_size>0: - self.num_addr_lines = 2**self.col_addr_size + self.row_addr_size + self.num_col_addr_lines = 2**self.col_addr_size + self.num_addr_lines = self.num_col_addr_lines + self.row_addr_size else: + self.num_col_addr_lines = 0 self.num_addr_lines = self.row_addr_size # M1/M2 routing pitch is based on contacted pitch self.m1_pitch = contact.m1m2.height + max(self.m1_space,self.m2_space) self.m2_pitch = contact.m2m3.height + max(self.m2_space,self.m3_space) - # Overall central bus gap. It includes all the column mux lines, - # control lines, and address flop to decoder lines - # 1.5 pitches on the right on the right of the control lines for vias (e.g. column mux addr lines) - self.start_of_right_central_bus = -self.m2_pitch * (self.num_control_lines + 1.5) - self.start_of_left_central_bus = self.start_of_right_central_bus - self.m2_pitch*(self.num_addr_lines) - # add a pitch on each side - self.overall_central_bus_width = self.m2_pitch * (self.num_control_lines + self.num_addr_lines + 3) + # The width of this bus is needed to place other modules (e.g. decoder) + self.central_bus_width = self.m2_pitch * (self.num_control_lines + self.num_addr_lines + 1) + + # The width of the bus below the decoder to route to the central bus + self.addr_bus_height = self.m1_pitch * (self.addr_size + 1) + @@ -189,20 +199,15 @@ class bank(design.design): self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size, - words_per_row=self.words_per_row) + words_per_row=self.words_per_row) self.add_mod(self.sense_amp_array) self.write_driver_array = self.mod_write_driver_array(columns=self.num_cols, word_size=self.word_size) self.add_mod(self.write_driver_array) - self.decoder = self.mod_decoder(rows=self.num_rows) - self.add_mod(self.decoder) - - self.msf_address = self.mod_ms_flop_array(name="msf_address", - columns=self.row_addr_size+self.col_addr_size, - word_size=self.row_addr_size+self.col_addr_size) - self.add_mod(self.msf_address) + self.row_decoder = self.mod_decoder(rows=self.num_rows) + self.add_mod(self.row_decoder) self.msf_data_in = self.mod_ms_flop_array(name="msf_data_in", columns=self.num_cols, @@ -245,7 +250,7 @@ class bank(design.design): """ Adding Precharge """ # The wells must be far enough apart - # The enclosure is for the well and the spacig is to the bitcell wells + # The enclosure is for the well and the spacing is to the bitcell wells y_offset = self.bitcell_array.height + 2*drc["pwell_to_nwell"] + drc["well_enclosure_active"] self.precharge_array_inst=self.add_inst(name="precharge_array", mod=self.precharge_array, @@ -361,12 +366,11 @@ class bank(design.design): # The predecoder is below the x-axis and the main decoder is above the x-axis # The address flop and decoder are aligned in the x coord. - decoder_x_offset = self.decoder.width + self.overall_central_bus_width - addr_x_offset = self.msf_address.height - offset = vector(max(decoder_x_offset, addr_x_offset), - self.decoder.predecoder_height) + decoder_x_offset = self.row_decoder.width + self.central_bus_width + offset = vector(decoder_x_offset, + self.row_decoder.predecoder_height) self.row_decoder_inst=self.add_inst(name="row_decoder", - mod=self.decoder, + mod=self.row_decoder, offset=offset.scale(-1,-1)) temp = [] @@ -383,7 +387,7 @@ class bank(design.design): # The wordline driver is placed to the right of the main decoder width. # This means that it slightly overlaps with the hierarchical decoder, # but it shares power rails. This may differ for other decoders later... - x_offset = self.decoder.width + self.overall_central_bus_width - self.decoder.row_decoder_width + x_offset = self.row_decoder.width + self.central_bus_width - self.row_decoder.row_decoder_width self.wordline_driver_inst=self.add_inst(name="wordline_driver", mod=self.wordline_driver, offset=vector(x_offset,0).scale(-1,-1)) @@ -398,73 +402,54 @@ class bank(design.design): temp.append("gnd") self.connect_inst(temp) - def add_msf_address(self): - """ Adding address wires """ - - # A gap between the hierarchical decoder and addr flops - gap = max(drc["pwell_to_nwell"], 2*self.m2_pitch) - - # The address flops go below the hierarchical decoder - decoder_x_offset = self.decoder.width + self.overall_central_bus_width - addr_x_offset = self.msf_address.height + self.overall_central_bus_width - # msf_address is not in the y-coord because it is rotated - offset = vector(max(decoder_x_offset, addr_x_offset), - self.decoder.predecoder_height + gap) - self.msf_address_inst=self.add_inst(name="address_flop_array", - mod=self.msf_address, - offset=offset.scale(-1,-1), - rotate=270) - temp = [] - for i in range(self.row_addr_size+self.col_addr_size): - temp.append("ADDR[{0}]".format(i)) - for i in range(self.row_addr_size+self.col_addr_size): - if self.col_addr_size==1 and i==self.row_addr_size: - temp.extend(["sel[1]","sel[0]"]) - else: - temp.extend(["A[{0}]".format(i),"A_bar[{0}]".format(i)]) - temp.append(self.prefix+"clk_buf") - temp.extend(["vdd", "gnd"]) - self.connect_inst(temp) - - def add_column_decoder(self): - """ Create a 2:4 decoder to decode column select lines if the col_addr_size = 4 """ - - if self.col_addr_size == 1: - return # This is done from the FF outputs directly - if self.col_addr_size == 2: - # FIXME: Should just load this rather than reference a level down - self.col_decoder = self.decoder.pre2_4 - elif self.col_addr_size == 3: - debug.error("8 way column mux not yet supported...", -1) - # FIXME: Should just load this rather than reference a level down - self.col_decoder = self.decoder.pre3_8 - else: - # No error checking before? - debug.error("Invalid column decoder?",-1) - - - # Place the col decoder just to the left of the control bus - gap = max(drc["pwell_to_nwell"], 2*self.m2_pitch) - x_off = gap + self.overall_central_bus_width + self.col_decoder.width - # Place the col decoder below the the address flops which are below the row decoder (lave some space for wells) - vertical_gap = max(drc["pwell_to_nwell"], 2*self.m2_pitch) - y_off = self.decoder.predecoder_height + self.msf_address.width + self.col_decoder.height + 2*vertical_gap + def add_column_decoder_module(self): + """ + Create a 2:4 or 3:8 column address decoder. + """ + # Place the col decoder aligned left to row decoder + x_off = -(self.central_bus_width + self.row_decoder.width) + y_off = -(self.row_decoder.predecoder_height + self.col_decoder.height + self.addr_bus_height) self.col_decoder_inst=self.add_inst(name="col_address_decoder", mod=self.col_decoder, - offset=vector(x_off,y_off).scale(-1,-1)) + offset=vector(x_off,y_off)) + temp = [] for i in range(self.col_addr_size): temp.append("A[{0}]".format(i + self.row_addr_size)) - for j in range(2**self.col_addr_size): + for j in range(self.num_col_addr_lines): temp.append("sel[{0}]".format(j)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) + + def add_column_decoder(self): + """ + Create a decoder to decode column select lines. This could be an inverter/buffer for 1:2, + 2:4 decoder, or 3:8 decoder. + """ + if self.col_addr_size == 0: + return + elif self.col_addr_size == 1: + self.col_decoder = pinvbuf() + self.add_mod(self.col_decoder) + elif self.col_addr_size == 2: + self.col_decoder = self.row_decoder.pre2_4 + elif self.col_addr_size == 3: + self.col_decoder = self.row_decoder.pre3_8 + else: + # No error checking before? + debug.error("Invalid column decoder?",-1) + + self.add_column_decoder_module() def add_bank_select(self): """ Instantiate the bank select logic. """ - xoffset = self.left_vdd_x_offset + self.supply_rail_pitch + + if not self.num_banks > 1: + return + + xoffset = -(self.central_bus_width + self.bank_select.width) # extra space to allow vias yoffset = self.min_point + 2*self.supply_rail_pitch + self.m1_space self.bank_select_pos = vector(xoffset,yoffset) @@ -492,7 +477,7 @@ class bank(design.design): for gated_name in self.control_signals: # Connect the inverter output to the central bus out_pos = self.bank_select_inst.get_pin(gated_name).rc() - bus_pos = vector(self.central_line_xoffset[gated_name], out_pos.y) + bus_pos = vector(self.bus_xoffset[gated_name], out_pos.y) self.add_path("metal3",[out_pos, bus_pos]) self.add_via_center(layers=("metal2", "via2", "metal3"), offset=bus_pos, @@ -506,34 +491,36 @@ class bank(design.design): def setup_layout_constraints(self): - """ Calculating layout constraints, width, height etc """ + """ After the modules are instantiated, find the dimensions for the + control bus, power ring, etc. """ #The minimum point is either the bottom of the address flops, #the column decoder (if there is one) or the tristate output #driver. # Leave room for the output below the tri gate. - tri_gate_min_point = self.tri_gate_array_inst.ll().y - 3*self.m2_pitch - addr_min_point = self.msf_address_inst.ll().y - 2*self.m2_pitch - - if self.col_addr_size >1: - self.decoder_min_point = self.col_decoder_inst.ll().y + tri_gate_min_point = self.tri_gate_array_inst.by() - 3*self.m2_pitch + row_decoder_min_point = self.row_decoder_inst.by() + if self.col_addr_size > 0: + col_decoder_min_point = self.col_decoder_inst.by() else: - self.decoder_min_point = addr_min_point - + col_decoder_min_point = row_decoder_min_point - self.addr_bus_height + + self.addr_min_point = row_decoder_min_point - self.addr_bus_height + if self.num_banks>1: # The control gating logic is below the decoder # Min of the control gating logic and tri gate. - self.min_point = min(self.decoder_min_point - self.bank_select.height, tri_gate_min_point) + self.min_point = min(col_decoder_min_point - self.bank_select.height, tri_gate_min_point) else: # Just the min of the decoder logic logic and tri gate. - self.min_point = min(self.decoder_min_point, addr_min_point, tri_gate_min_point) + self.min_point = min(col_decoder_min_point, tri_gate_min_point) # The max point is always the top of the precharge bitlines # Add a vdd and gnd power rail above the array self.max_point = self.precharge_array_inst.uy() + 3*self.m1_width # Create the core bbox for the power rings ur = vector(self.bitcell_array_inst.ur().x + 3*self.m1_width, self.max_point) - ll = vector(min(self.msf_address_inst.ll().x, self.row_decoder_inst.ll().x), self.min_point) + ll = vector(self.row_decoder_inst.lx(), self.min_point) self.core_bbox = [ll, ur] # Compute the vertical rail positions for later use @@ -550,145 +537,62 @@ class bank(design.design): self.height = self.max_point - self.min_point self.width = self.right_vdd_x_offset - self.left_gnd_x_offset + self.supply_rail_width - def route_power_ring(self, bbox): - """ Create vdd and gnd power rings around an area of the bounding box argument.. """ - - [ll, ur] = bbox - - # LEFT vertical rails - offset = ll.scale(1,0) + vector(-2*self.supply_rail_pitch, self.min_point) - left_gnd_pin=self.add_layout_pin(text="gnd", - layer="metal2", - offset=offset, - width=self.supply_rail_width, - height=self.height) - - - offset = ll.scale(1,0) + vector(-1*self.supply_rail_pitch, self.min_point) - left_vdd_pin=self.add_layout_pin(text="vdd", - layer="metal2", - offset=offset, - width=self.supply_rail_width, - height=self.height) - - # RIGHT vertical rails - offset = ur.scale(1,0) + vector(0,self.min_point) - right_gnd_pin = self.add_layout_pin(text="gnd", - layer="metal2", - offset=offset, - width=self.supply_rail_width, - height=self.height) - - offset = ur.scale(1,0) + vector(self.supply_rail_pitch,self.min_point) - right_vdd_pin=self.add_layout_pin(text="vdd", - layer="metal2", - offset=offset, - width=self.supply_rail_width, - height=self.height) - - # BOTTOM horizontal rails - offset = ll + vector(-2*self.supply_rail_pitch, -2*self.supply_rail_pitch) - bottom_gnd_pin=self.add_layout_pin(text="gnd", - layer="metal1", - offset=offset, - width=self.width, - height=self.supply_rail_width) - - offset = ll + vector(-2*self.supply_rail_pitch, -1*self.supply_rail_pitch) - bottom_vdd_pin=self.add_layout_pin(text="vdd", - layer="metal1", - offset=offset, - width=self.width, - height=self.supply_rail_width) - - # TOP horizontal rails - offset = vector(ll.x, ur.y) + vector(-2*self.supply_rail_pitch,0) - top_gnd_pin=self.add_layout_pin(text="gnd", - layer="metal1", - offset=offset, - width=self.width, - height=self.supply_rail_width) - - offset = vector(ll.x, ur.y) + vector(-2*self.supply_rail_pitch, self.supply_rail_pitch) - top_vdd_pin=self.add_layout_pin(text="vdd", - layer="metal1", - offset=offset, - width=self.width, - height=self.supply_rail_width) - - # Remember these for connecting things in the design - self.left_gnd_x_center = left_gnd_pin.cx() - self.left_vdd_x_center = left_vdd_pin.cx() - self.right_gnd_x_center = right_gnd_pin.cx() - self.right_vdd_x_center = right_vdd_pin.cx() - - self.bottom_gnd_y_center = bottom_gnd_pin.cy() - self.bottom_vdd_y_center = bottom_vdd_pin.cy() - self.top_gnd_y_center = top_gnd_pin.cy() - self.top_vdd_y_center = top_vdd_pin.cy() - - via_points = [vector(self.left_gnd_x_center, self.bottom_gnd_y_center), - vector(self.left_gnd_x_center, self.top_gnd_y_center), - vector(self.right_gnd_x_center, self.bottom_gnd_y_center), - vector(self.right_gnd_x_center, self.top_gnd_y_center), - vector(self.left_vdd_x_center, self.bottom_vdd_y_center), - vector(self.left_vdd_x_center, self.top_vdd_y_center), - vector(self.right_vdd_x_center, self.bottom_vdd_y_center), - vector(self.right_vdd_x_center, self.top_vdd_y_center)] - - for pt in via_points: - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=pt, - size = (3,3)) - - - def create_central_bus(self): + def route_central_bus(self): """ Create the address, supply, and control signal central bus lines. """ - # Address lines in central line connection are 2*col_addr_size - # number of connections for the column mux (for both signal and _bar) and row_addr_size (no _bar) + # Overall central bus width. It includes all the column mux lines, + # control lines, and address flop to decoder lines. + # 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_x_offset = -self.m2_pitch * (self.num_control_lines) + address_bus_x_offset = control_bus_x_offset - self.m2_pitch*(self.num_addr_lines) - self.central_line_xoffset = {} + # Track the bus offsets for other modules to access + self.bus_xoffset = {} - # Control lines (to the right of the GND rail) + # Control lines for i in range(self.num_control_lines): - x_offset = self.start_of_right_central_bus + i*self.m2_pitch - self.central_line_xoffset[self.control_signals[i]]=x_offset + 0.5*self.m2_width + x_offset = control_bus_x_offset + i*self.m2_pitch + # Make the xoffset map the center of the rail + self.bus_xoffset[self.control_signals[i]]=x_offset + 0.5*self.m2_width # Pins are added later if this is a single bank, so just add rectangle now self.add_rect(layer="metal2", offset=vector(x_offset, self.min_point), width=self.m2_width, height=self.height) - # row address lines (to the left of the column mux or GND rail) - # goes from 0 down to the bottom of the address flops + # Row address lines (to left of col address lines) + # goes from bottom of bitcell array down to the bottom of the column decoder/addresses for i in range(self.row_addr_size): - x_offset = self.start_of_left_central_bus + i*self.m2_pitch - name = "A[{}]".format(i) - self.central_line_xoffset[name]=x_offset + 0.5*self.m2_width + addr_idx = i + self.col_addr_size + x_offset = address_bus_x_offset + i*self.m2_pitch + name = "A[{}]".format(addr_idx) + # Make the xoffset map the center of the rail + self.bus_xoffset[name]=x_offset + 0.5*self.m2_width # Add a label pin for LVS correspondence and visual help inspecting the rail. self.add_label_pin(text=name, layer="metal2", - offset=vector(x_offset, self.decoder_min_point), + offset=vector(x_offset, self.addr_min_point), width=self.m2_width, - height=-self.decoder_min_point) + height=-self.addr_min_point) - # column mux lines if there is column mux [2 or 4 lines] (to the left of the GND rail) - # goes from 0 down to the min point + # Column mux lines if there is column mux + # goes from bottom of bitcell array down to the bottom of the column decoder/addresses if self.col_addr_size>0: - for i in range(2**self.col_addr_size): - x_offset = self.start_of_left_central_bus + (i+self.row_addr_size)*self.m2_pitch - name = "sel[{}]".format(i) - self.central_line_xoffset[name]=x_offset + 0.5*self.m2_width + for i in range(self.num_col_addr_lines): + x_offset = address_bus_x_offset + (i+self.row_addr_size)*self.m2_pitch + name = "sel[{}]".format(i) # One hot select signals + # Make the xoffset map the center of the rail + self.bus_xoffset[name]=x_offset + 0.5*self.m2_width # Add a label pin for LVS correspondence self.add_label_pin(text=name, layer="metal2", - offset=vector(x_offset, self.decoder_min_point), + offset=vector(x_offset, self.col_decoder_inst.by()), width=self.m2_width, - height=-self.decoder_min_point) + height=-self.col_decoder_inst.by()) def route_precharge_to_bitcell_array(self): @@ -758,10 +662,10 @@ class bank(design.design): def route_row_decoder(self): """ Routes the row decoder inputs and supplies """ - for i in range(self.row_addr_size): + addr_idx = i + self.col_addr_size # before this index, we are using 2x4 decoders - switchover_index = 2*self.decoder.no_of_pre2x4 + switchover_index = 2*self.row_decoder.no_of_pre2x4 # so decide what modulus to perform the height spacing if i < switchover_index: position_heights = i % 2 @@ -770,8 +674,10 @@ class bank(design.design): # Connect the address rails to the decoder # Note that the decoder inputs are long vertical rails so spread out the connections vertically. - decoder_in_position = self.row_decoder_inst.get_pin("A[{}]".format(i)).lr() + vector(0,position_heights*self.bitcell.height+self.m2_pitch) - rail_position = vector(self.central_line_xoffset["A[{}]".format(i)],decoder_in_position.y) + decoder_in_position = self.row_decoder_inst.get_pin("A[{}]".format(i)).lr() \ + + vector(0,position_heights*self.bitcell.height+self.m2_pitch) + rail_position = vector(self.bus_xoffset["A[{}]".format(addr_idx)], + decoder_in_position.y) self.add_path("metal1",[decoder_in_position,rail_position]) decoder_in_via = decoder_in_position - vector(0,0.5*self.m2_width) @@ -841,156 +747,101 @@ class bank(design.design): # Connect the select lines to the column mux # These must be in metal3 so that they don't overlap any gnd lines from decoders - for i in range(2**self.col_addr_size): + for i in range(self.num_col_addr_lines): name = "sel[{}]".format(i) mux_addr_pos = self.col_mux_array_inst.get_pin(name).lc() - wire_pos = vector(self.central_line_xoffset[name], mux_addr_pos.y) + wire_pos = vector(self.bus_xoffset[name], mux_addr_pos.y) self.add_path("metal1", [wire_pos,mux_addr_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=wire_pos, rotate=90) - - # Take care of the column address decoder routing - # If there is a 2:4 decoder for column select lines - # or TODO 3:8 decoder should work too! - if self.col_addr_size > 1: - # connections between outputs of decoder to the extension of - # main address bus - for i in range(2**self.col_addr_size): + if self.col_addr_size == 1: + + decode_out_pos = self.col_decoder_inst.get_pin("Zb").rc() + selx_pos = vector(self.bus_xoffset["sel[0]"],decode_out_pos.y) + self.add_path("metal1",[decode_out_pos, selx_pos]) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=selx_pos, + rotate=90) + decode_out_pos = self.col_decoder_inst.get_pin("Z").rc() + selx_pos = vector(self.bus_xoffset["sel[1]"],decode_out_pos.y) + self.add_path("metal1",[decode_out_pos, selx_pos]) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=selx_pos, + rotate=90) + + # The Address LSB + decode_in_pin = self.col_decoder_inst.get_pin("A") + pin_pos = vector(self.left_gnd_x_offset, decode_in_pin.cy()) + self.add_layout_pin_center_segment(text="A[0]", + layer="metal1", + start=pin_pos, + end=decode_in_pin.lc()) + + elif self.col_addr_size > 1: + # Route the col decoder outputs to the col select bus + for i in range(self.num_col_addr_lines): name = "sel[{}]".format(i) - x_offset = self.central_line_xoffset[name] decode_out_pos = self.col_decoder_inst.get_pin("out[{}]".format(i)).rc() - selx_pos = vector(self.central_line_xoffset[name],decode_out_pos.y) + selx_pos = vector(self.bus_xoffset[name],decode_out_pos.y) self.add_path("metal1",[decode_out_pos, selx_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=selx_pos, rotate=90) - # route the gnd rails, add contact to rail as well - for gnd_pin in self.col_decoder_inst.get_pins("gnd"): - left_rail_pos = vector(self.left_gnd_x_offset, gnd_pin.cy()) - self.add_path("metal1", [left_rail_pos, gnd_pin.rc()]) + # Route from the col decoder up to the address bus + for i in range(self.col_addr_size): + name = "A[{}]".format(i) + decode_in_pin = self.col_decoder_inst.get_pin("in[{}]".format(i)) + addr_pin = self.get_pin(name) + addr_pos = vector(decode_in_pin.cx(), addr_pin.by() + 0.5*self.m1_width) + self.add_path("metal2", [addr_pos, decode_in_pin.uc()]) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left_rail_pos, - size = (1,3), - rotate=90) - - # route the vdd rails - for vdd_pin in self.col_decoder_inst.get_pins("vdd"): - left_rail_pos = vector(self.left_vdd_x_center, vdd_pin.cy()) - self.add_path("metal1", [left_rail_pos, vdd_pin.rc()]) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left_rail_pos, - size = (1,3), - rotate=90) + offset=addr_pos, + rotate=90) + + + # route the gnd rails, add contact to rail as well + for gnd_pin in self.col_decoder_inst.get_pins("gnd"): + left_rail_pos = vector(self.left_gnd_x_center, gnd_pin.cy()) + self.add_path("metal1", [left_rail_pos, gnd_pin.rc()]) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=left_rail_pos, + size = (1,3), + rotate=90) + + # route the vdd rails + for vdd_pin in self.col_decoder_inst.get_pins("vdd"): + left_rail_pos = vector(self.left_vdd_x_center, vdd_pin.cy()) + self.add_path("metal1", [left_rail_pos, vdd_pin.rc()]) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=left_rail_pos, + size = (1,3), + rotate=90) - # The connection between last address flops to the input - # of the column_mux line decoder - for i in range(self.col_addr_size): - ff_index = i + self.row_addr_size - dout_pos = self.msf_address_inst.get_pin("dout[{}]".format(ff_index)).rc() - in_pos = self.col_decoder_inst.get_pin("in[{}]".format(i)).uc() - mid_pos = vector(in_pos.x,dout_pos.y) - self.add_path("metal3",[dout_pos, mid_pos, in_pos]) - - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=dout_pos, - rotate=90) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=in_pos) - - - - - - - # if there are only two column select lines we just connect the dout_bar of the last FF - # to only select line and dout of that FF to the other select line - elif self.col_addr_size == 1: - - dout_bar_pos = self.msf_address_inst.get_pin("dout_bar[{}]".format(self.row_addr_size)).rc() - sel0_pos = vector(self.central_line_xoffset["sel[0]"],dout_bar_pos.y) - self.add_path("metal1",[dout_bar_pos, sel0_pos]) - # two vias on both ends - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=dout_bar_pos, - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=sel0_pos, - rotate=90) - - dout_pos = self.msf_address_inst.get_pin("dout[{}]".format(self.row_addr_size)).rc() - sel1_pos = vector(self.central_line_xoffset["sel[1]"],dout_pos.y) - self.add_path("metal1",[dout_pos, sel1_pos]) - # two vias on both ends - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=dout_pos, - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=sel1_pos, - rotate=90) - - - def route_msf_address(self): + def route_address(self): """ Routing the row address lines from the address ms-flop array to the row-decoder """ - # Create the address input pins + for i in range(self.addr_size): - msf_din_pins = self.msf_address_inst.get_pins("din[{}]".format(i)) - for pin in msf_din_pins: - if pin.layer=="metal3": - msf_din_pos=pin.ll() - break - address_pos = vector(self.left_gnd_x_offset, msf_din_pos.y) - self.add_layout_pin(text="ADDR[{}]".format(i), - layer="metal3", - offset=address_pos, - width=msf_din_pos.x - address_pos.x) - - - for i in range(self.row_addr_size): - - # Connect the ff outputs to the rails - dout_pos = self.msf_address_inst.get_pin("dout[{}]".format(i)).rc() - rail_pos = vector(self.central_line_xoffset["A[{}]".format(i)],dout_pos.y) - self.add_path("metal1",[dout_pos, rail_pos]) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=dout_pos, - rotate=90) + name = "A[{}]".format(i) + address_y_offset = self.addr_min_point + (i+1)*self.m1_pitch + pin_pos = vector(self.left_gnd_x_offset, address_y_offset) + if name in self.bus_xoffset: + rail_pos = vector(self.bus_xoffset[name],pin_pos.y) + else: + # Route to right of col decoder if it's not part of central bus + rail_pos = vector(self.col_decoder_inst.rx(),pin_pos.y) + self.add_layout_pin_center_segment(text=name, + layer="metal1", + start=pin_pos, + end=rail_pos) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=rail_pos, rotate=90) - # Connect address FF gnd - for gnd_pin in self.msf_address_inst.get_pins("gnd"): - if gnd_pin.layer != "metal2": - continue - gnd_via = gnd_pin.ll() + vector(contact.m1m2.height,0) - self.add_via(layers=("metal1", "via1", "metal2"), - offset=gnd_via, - rotate=90) - gnd_offset = gnd_pin.lc() - rail_offset = vector(self.left_gnd_x_center, gnd_offset.y) - self.add_path("metal1",[gnd_offset,rail_offset]) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=rail_offset, - size = (1,3), - rotate=90) - - # Connect address FF vdd - for vdd_pin in self.msf_address_inst.get_pins("vdd"): - if vdd_pin.layer != "metal1": - continue - vdd_offset = vdd_pin.bc() - mid = vector(vdd_offset.x, vdd_offset.y - self.m1_pitch) - rail_offset = vector(self.left_vdd_x_center, mid.y) - self.add_path("metal1", [vdd_offset,mid,rail_offset]) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=rail_offset, - size = (1,3), - rotate=90) def add_lvs_correspondence_points(self): """ This adds some points for easier debugging if LVS goes wrong. @@ -1051,27 +902,17 @@ class bank(design.design): connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc())) for (control_signal, pin_pos) in connection: - control_pos = vector(self.central_line_xoffset[control_signal], pin_pos.y) + control_pos = vector(self.bus_xoffset[control_signal], pin_pos.y) self.add_path("metal1", [control_pos, pin_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=control_pos, rotate=90) - # clk to msf address - control_signal = self.prefix+"clk_buf" - pin_pos = self.msf_address_inst.get_pin("clk").uc() - mid_pos = pin_pos + vector(0,self.m1_pitch) - control_pos = vector(self.central_line_xoffset[control_signal], mid_pos.y) - self.add_path("metal1",[pin_pos, mid_pos, control_pos]) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=control_pos, - rotate=90) - # clk to wordline_driver control_signal = self.prefix+"clk_buf" pin_pos = self.wordline_driver_inst.get_pin("en").uc() mid_pos = pin_pos + vector(0,self.m1_pitch) - control_x_offset = self.central_line_xoffset[control_signal] + control_x_offset = self.bus_xoffset[control_signal] control_pos = vector(control_x_offset + self.m1_width, mid_pos.y) self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) control_via_pos = vector(control_x_offset, mid_pos.y) @@ -1162,18 +1003,19 @@ class bank(design.design): """ Add the control signal input pins """ for ctrl in self.control_signals: - x_offset = self.central_line_xoffset[ctrl] + # xoffsets are the center of the rail + x_offset = self.bus_xoffset[ctrl] - 0.5*self.m2_width if self.num_banks > 1: # it's not an input pin if we have multiple banks self.add_label_pin(text=ctrl, layer="metal2", - offset=vector(x_offset - 0.5*self.m2_width, self.min_point), + offset=vector(x_offset, self.min_point), width=self.m2_width, height=self.height) else: self.add_layout_pin(text=ctrl, layer="metal2", - offset=vector(x_offset - 0.5*self.m2_width, self.min_point), + offset=vector(x_offset, self.min_point), width=self.m2_width, height=self.height) @@ -1206,9 +1048,9 @@ class bank(design.design): def analytical_delay(self, slew, load): """ return analytical delay of the bank""" - msf_addr_delay = self.msf_address.analytical_delay(slew, self.decoder.input_load()) + msf_addr_delay = self.msf_address.analytical_delay(slew, self.row_decoder.input_load()) - decoder_delay = self.decoder.analytical_delay(msf_addr_delay.slew, self.wordline_driver.input_load()) + decoder_delay = self.row_decoder.analytical_delay(msf_addr_delay.slew, self.wordline_driver.input_load()) word_driver_delay = self.wordline_driver.analytical_delay(decoder_delay.slew, self.bitcell_array.input_load()) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index f923f1f8..69b6c47c 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -33,6 +33,9 @@ class bitcell_array(design.design): self.add_pins() self.create_layout() self.add_layout_pins() + + self.offset_all_coordinates() + self.DRC_LVS() def add_pins(self): diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index c9dbd436..354c33f5 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -7,6 +7,7 @@ from pinv import pinv from pnand2 import pnand2 from pnand3 import pnand3 from pnor2 import pnor2 +from pinvbuf import pinvbuf import math from vector import vector from globals import OPTS @@ -30,7 +31,7 @@ class control_logic(design.design): self.create_modules() self.setup_layout_offsets() self.add_modules() - self.add_routing() + #self.add_routing() def create_modules(self): """ add all the required modules """ @@ -48,23 +49,18 @@ class control_logic(design.design): self.add_mod(self.nor2) # Special gates: inverters for buffering + self.clkbuf = pinvbuf(4,16) + self.add_mod(self.clkbuf) self.inv = self.inv1 = pinv(1) self.add_mod(self.inv1) - self.inv2 = pinv(2) - self.add_mod(self.inv2) - self.inv4 = pinv(4) - self.add_mod(self.inv4) - self.inv8 = pinv(8) - self.add_mod(self.inv8) - self.inv16 = pinv(16) - self.add_mod(self.inv16) - - c = reload(__import__(OPTS.ms_flop_array)) - ms_flop_array = getattr(c, OPTS.ms_flop_array) - self.msf_control = ms_flop_array(name="msf_control", - columns=3, - word_size=3) - self.add_mod(self.msf_control) + # self.inv2 = pinv(2) + # self.add_mod(self.inv2) + # self.inv4 = pinv(4) + # self.add_mod(self.inv4) + # self.inv8 = pinv(8) + # self.add_mod(self.inv8) + # self.inv16 = pinv(16) + # self.add_mod(self.inv16) c = reload(__import__(OPTS.replica_bitline)) replica_bitline = getattr(c, OPTS.replica_bitline) @@ -91,8 +87,8 @@ class control_logic(design.design): # First RAIL Parameters: gnd, oe, oebar, cs, we, clk_buf, clk_bar self.rail_1_start_x = 0 - self.num_rails_1 = 8 - self.rail_1_names = ["clk_buf", "gnd", "oe_bar", "cs", "we", "vdd", "oe", "clk_bar"] + self.num_rails_1 = 7 + self.rail_1_names = ["clk_buf", "gnd", "cs", "we", "vdd", "oe", "clk_bar"] self.overall_rail_1_gap = (self.num_rails_1 + 2) * self.m2_pitch self.rail_1_x_offsets = {} @@ -103,18 +99,21 @@ class control_logic(design.design): def add_modules(self): """ Place all the modules """ - self.add_control_flops() - self.add_clk_buffer(0) - self.add_1st_row(0) - self.add_2nd_row(self.inv1.height) - self.add_3rd_row(2*self.inv1.height) - self.add_control_routing() - self.add_rbl(0) + + self.add_clk_buffer(row=0) # 0 and 1st row + self.add_oe_row(row=2) + self.add_sen_row(row=3) + self.add_we_row(row=4) + + #self.add_control_routing() + + self.add_rbl(row=5) + self.add_layout_pins() self.add_lvs_correspondence_points() - self.height = max(self.replica_bitline.width, 3 * self.inv1.height, self.msf_offset.y) + self.height = max(self.replica_bitline.width, 4 * self.inv1.height) self.width = self.replica_bitline_offset.x + self.replica_bitline.height @@ -122,7 +121,7 @@ class control_logic(design.design): def add_routing(self): """ Routing between modules """ - self.add_clk_routing() + #self.add_clk_routing() self.add_trien_routing() self.add_rblk_routing() self.add_wen_routing() @@ -130,116 +129,117 @@ class control_logic(design.design): self.add_output_routing() self.add_supply_routing() - def add_control_flops(self): - """ Add the control signal flops for OEb, WEb, CSb. """ - self.msf_offset = vector(0, self.inv.height+self.msf_control.width+2*self.m2_pitch) - self.msf_inst=self.add_inst(name="msf_control", - mod=self.msf_control, - offset=self.msf_offset, - rotate=270) - # don't change this order. This pins are meant for internal connection of msf array inside the control logic. - # These pins are connecting the msf_array inside of control_logic. - temp = ["oeb", "csb", "web", - "oe_bar", "oe", - "cs_bar", "cs", - "we_bar", "we", - "clk_buf", "vdd", "gnd"] - self.connect_inst(temp) - def add_rbl(self,y_off): + def add_rbl(self,row): """ Add the replica bitline """ - - # Add to the right of the control rows and routing channel - rows_end_x = max (self.row_1_end_x, self.row_2_end_x, self.row_3_end_x) + y_off = row * self.inv1.height - self.replica_bitline_offset = vector(rows_end_x , y_off) + # Add the RBL above the rows + # Add to the right of the control rows and routing channel + self.replica_bitline_offset = vector(0, y_off) self.rbl=self.add_inst(name="replica_bitline", mod=self.replica_bitline, - offset=self.replica_bitline_offset, - mirror="MX", - rotate=90) + offset=self.replica_bitline_offset) self.connect_inst(["rblk", "pre_s_en", "vdd", "gnd"]) def add_layout_pins(self): """ Add the input/output layout pins. """ - # Top to bottom: CS WE OE signal groups - pin_set = ["oeb","csb","web"] - for (i,pin_name) in zip(range(3),pin_set): - subpin_name="din[{}]".format(i) - pins=self.msf_inst.get_pins(subpin_name) - for pin in pins: - if pin.layer=="metal3": - self.add_layout_pin(text=pin_name, - layer="metal3", - offset=pin.ll(), - width=pin.width(), - height=pin.height()) + # # Top to bottom: CS WE OE signal groups + # pin_set = ["oeb","csb","web"] + # for (i,pin_name) in zip(range(3),pin_set): + # subpin_name="din[{}]".format(i) + # pins=self.msf_inst.get_pins(subpin_name) + # for pin in pins: + # if pin.layer=="metal3": + # self.add_layout_pin(text=pin_name, + # layer="metal3", + # offset=pin.ll(), + # width=pin.width(), + # height=pin.height()) - pin=self.clk_inv1.get_pin("A") + pin=self.clkbuf.get_pin("A") self.add_layout_pin(text="clk", layer="metal1", offset=pin.ll().scale(0,1), width=pin.rx(), height=pin.height()) - pin=self.clk_inv1.get_pin("gnd") - self.add_layout_pin(text="gnd", - layer="metal1", - offset=pin.ll(), - width=self.width) + # pin=self.clkbuf.get_pin("gnd") + # self.add_layout_pin(text="gnd", + # layer="metal1", + # offset=pin.ll(), + # width=self.width) - pin=self.clk_inv1.get_pin("vdd") - self.add_layout_pin(text="vdd", - layer="metal1", - offset=pin.ll(), - width=self.width) + # pin=self.clkbuf.get_pin("vdd") + # self.add_layout_pin(text="vdd", + # layer="metal1", + # offset=pin.ll(), + # width=self.width) - def add_clk_buffer(self,y_off): + def add_clk_buffer(self,row): """ Add the multistage clock buffer below the control flops """ - # 4 stage clock buffer - self.clk_inv1_offset = vector(0, y_off) - self.clk_inv1=self.add_inst(name="inv_clk1_bar", - mod=self.inv2, - offset=self.clk_inv1_offset) - self.connect_inst(["clk", "clk1_bar", "vdd", "gnd"]) - self.clk_inv2_offset = self.clk_inv1_offset + vector(self.inv2.width,0) - self.clk_inv2=self.add_inst(name="inv_clk2", - mod=self.inv4, - offset=self.clk_inv2_offset) - self.connect_inst(["clk1_bar", "clk2", "vdd", "gnd"]) - self.clk_bar_offset = self.clk_inv2_offset + vector(self.inv4.width,0) - self.clk_bar=self.add_inst(name="inv_clk_bar", - mod=self.inv8, - offset=self.clk_bar_offset) - self.connect_inst(["clk2", "clk_bar", "vdd", "gnd"]) - self.clk_buf_offset = self.clk_bar_offset + vector(self.inv8.width,0) - self.clk_buf=self.add_inst(name="inv_clk_buf", - mod=self.inv16, - offset=self.clk_buf_offset) - self.connect_inst(["clk_bar", "clk_buf", "vdd", "gnd"]) + y_off = row*self.inv1.height + if row % 2: + y_off += self.clkbuf.height + mirror="MX" + else: + mirror="R0" - # Connect between the inverters - self.add_path("metal1", [self.clk_inv1.get_pin("Z").center(), - self.clk_inv2.get_pin("A").center()]) - self.add_path("metal1", [self.clk_inv2.get_pin("Z").center(), - self.clk_bar.get_pin("A").center()]) - self.add_path("metal1", [self.clk_bar.get_pin("Z").center(), - self.clk_buf.get_pin("A").center()]) + clkbuf_offset = vector(0,y_off) + self.clkbuf_inst = self.add_inst(name="clkbuf", + mod=self.clkbuf, + offset=clkbuf_offset) + + self.connect_inst(["clk","clk_bar","clk","vdd","gnd"]) + + # # 4 stage clock buffer + # self.clk_inv1_offset = vector(self.rail_1_start_x, y_off) + # self.clk_inv1=self.add_inst(name="inv_clk1_bar", + # mod=self.inv2, + # offset=self.clk_inv1_offset) + # self.connect_inst(["clk", "clk1_bar", "vdd", "gnd"]) + # self.clk_inv2_offset = self.clk_inv1_offset + vector(self.inv2.width,0) + # self.clk_inv2=self.add_inst(name="inv_clk2", + # mod=self.inv4, + # offset=self.clk_inv2_offset) + # self.connect_inst(["clk1_bar", "clk2", "vdd", "gnd"]) + # self.clk_bar_offset = self.clk_inv2_offset + vector(self.inv4.width,0) + # self.clk_bar=self.add_inst(name="inv_clk_bar", + # mod=self.inv8, + # offset=self.clk_bar_offset) + # self.connect_inst(["clk2", "clk_bar", "vdd", "gnd"]) + # self.clk_buf_offset = self.clk_bar_offset + vector(self.inv8.width,0) + # self.clk_buf=self.add_inst(name="inv_clk_buf", + # mod=self.inv16, + # offset=self.clk_buf_offset) + # self.connect_inst(["clk_bar", "clk_buf", "vdd", "gnd"]) + + # # Connect between the inverters + # self.add_path("metal1", [self.clk_inv1.get_pin("Z").center(), + # self.clk_inv2.get_pin("A").center()]) + # self.add_path("metal1", [self.clk_inv2.get_pin("Z").center(), + # self.clk_bar.get_pin("A").center()]) + # self.add_path("metal1", [self.clk_bar.get_pin("Z").center(), + # self.clk_buf.get_pin("A").center()]) - # This is the first rail offset - self.rail_1_start_x = max(self.msf_offset.x + self.msf_control.height,self.clk_buf_offset.x+self.inv16.width) + self.m2_pitch - def add_1st_row(self,y_off): - - x_off = self.rail_1_start_x + self.overall_rail_1_gap + def add_rblk_row(self,row): + x_off = 0 + y_off = row*self.inv1.height + if row % 2: + y_off += self.inv1.height + mirror="MX" + else: + mirror="R0" # input: OE, clk_bar,CS output: rblk_bar self.rblk_bar_offset = vector(x_off, y_off) self.rblk_bar=self.add_inst(name="nand3_rblk_bar", mod=self.nand3, - offset=self.rblk_bar_offset) + offset=self.rblk_bar_offset, + mirror=mirror) self.connect_inst(["clk_bar", "oe", "cs", "rblk_bar", "vdd", "gnd"]) x_off += self.nand3.width @@ -247,36 +247,21 @@ class control_logic(design.design): self.rblk_offset = vector(x_off, y_off) self.rblk=self.add_inst(name="inv_rblk", mod=self.inv1, - offset=self.rblk_offset) + offset=self.rblk_offset, + mirror=mirror) self.connect_inst(["rblk_bar", "rblk", "vdd", "gnd"]) #x_off += self.inv1.width - self.row_1_end_x = x_off + self.row_rblk_end_x = x_off - def add_2nd_row(self, y_off): - # start after first rails - x_off = self.rail_1_start_x + self.overall_rail_1_gap - y_off += self.inv1.height - - # input: clk_buf, OE_bar output: tri_en - self.tri_en_offset = vector(x_off, y_off) - self.tri_en=self.add_inst(name="nor2_tri_en", - mod=self.nor2, - offset=self.tri_en_offset, - mirror="MX") - self.connect_inst(["clk_buf", "oe_bar", "tri_en", "vdd", "gnd"]) - x_off += self.nor2.width + self.cell_gap - - # input: OE, clk_bar output: tri_en_bar - self.tri_en_bar_offset = vector(x_off,y_off) - self.tri_en_bar=self.add_inst(name="nand2_tri_en", - mod=self.nand2, - offset=self.tri_en_bar_offset, - mirror="MX") - self.connect_inst(["clk_bar", "oe", "tri_en_bar", "vdd", "gnd"]) - x_off += self.nand2.width - - x_off += self.inv1.width + self.cell_gap + def add_sen_row(self,row): + x_off = 0 + y_off = row*self.inv1.height + if row % 2: + y_off += self.inv1.height + mirror="MX" + else: + mirror="R0" # BUFFER INVERTERS FOR S_EN # input: input: pre_s_en_bar, output: s_en @@ -284,7 +269,7 @@ class control_logic(design.design): self.s_en=self.add_inst(name="inv_s_en", mod=self.inv1, offset=self.s_en_offset, - mirror="XY") + mirror=mirror) self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"]) x_off += self.inv1.width @@ -293,25 +278,70 @@ class control_logic(design.design): self.pre_s_en_bar=self.add_inst(name="inv_pre_s_en_bar", mod=self.inv1, offset=self.pre_s_en_bar_offset, - mirror="XY") + mirror=mirror) self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"]) #x_off += self.inv1.width + self.row_sen_end_x = x_off + + def add_oe_row(self, row): + x_off = 0 + y_off = row*self.inv1.height + if row % 2: + y_off += self.inv1.height + mirror="MX" + else: + mirror="R0" - self.row_2_end_x = x_off + # input: oe output: oe_bar + self.oe_inv_offset = vector(x_off, y_off) + self.oe_inv=self.add_inst(name="oe_inv", + mod=self.inv1, + offset=self.oe_inv_offset, + mirror=mirror) + self.connect_inst(["oe", "oe_bar", "vdd", "gnd"]) + x_off += self.inv1.width + self.cell_gap + - def add_3rd_row(self, y_off): - # start after first rails - x_off = self.rail_1_start_x + self.overall_rail_1_gap + # input: clk_buf, OE_bar output: tri_en + self.tri_en_offset = vector(x_off, y_off) + self.tri_en=self.add_inst(name="nor2_tri_en", + mod=self.nor2, + offset=self.tri_en_offset, + mirror=mirror) + self.connect_inst(["clk_buf", "oe_bar", "tri_en", "vdd", "gnd"]) + x_off += self.nor2.width + self.cell_gap + + # input: OE, clk_bar output: tri_en_bar + self.tri_en_bar_offset = vector(x_off,y_off) + self.tri_en_bar=self.add_inst(name="nand2_tri_en", + mod=self.nand2, + offset=self.tri_en_bar_offset, + mirror=mirror) + self.connect_inst(["clk_bar", "oe", "tri_en_bar", "vdd", "gnd"]) + #x_off += self.nand2.width + + #x_off += self.inv1.width + self.cell_gap - # This prevents some M2 outputs from overlapping (hack) - x_off += self.inv1.width + + + self.row_oe_end_x = x_off + + def add_we_row(self,row): + x_off = 0 + y_off = row*self.inv1.height + if row % 2: + y_off += self.inv1.height + mirror="MX" + else: + mirror="R0" # input: WE, clk_bar, CS output: w_en_bar self.w_en_bar_offset = vector(x_off, y_off) self.w_en_bar=self.add_inst(name="nand3_w_en_bar", mod=self.nand3, - offset=self.w_en_bar_offset) + offset=self.w_en_bar_offset, + mirror=mirror) self.connect_inst(["clk_bar", "cs", "we", "w_en_bar", "vdd", "gnd"]) x_off += self.nand3.width @@ -319,7 +349,9 @@ class control_logic(design.design): self.pre_w_en_offset = vector(x_off, y_off) self.pre_w_en=self.add_inst(name="inv_pre_w_en", mod=self.inv1, - offset=self.pre_w_en_offset) + offset=self.pre_w_en_offset, + mirror=mirror) + self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"]) x_off += self.inv1.width @@ -328,71 +360,65 @@ class control_logic(design.design): self.pre_w_en_bar_offset = vector(x_off, y_off) self.pre_w_en_bar=self.add_inst(name="inv_pre_w_en_bar", mod=self.inv1, - offset=self.pre_w_en_bar_offset) + offset=self.pre_w_en_bar_offset, + mirror=mirror) self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"]) x_off += self.inv1.width self.w_en_offset = vector(x_off, y_off) self.w_en=self.add_inst(name="inv_w_en2", mod=self.inv1, - offset=self.w_en_offset) + offset=self.w_en_offset, + mirror=mirror) self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"]) #x_off += self.inv1.width - self.row_3_end_x = x_off + self.row_we_end_x = x_off - def add_control_routing(self): - """ Route the vertical rails for internal control signals """ + # def add_control_routing(self): + # """ Route the vertical rails for internal control signals """ - control_rail_height = max(3 * self.inv1.height, self.msf_offset.y) + # control_rail_height = 3*self.inv1.height - for i in range(self.num_rails_1): - offset = vector(self.rail_1_start_x + (i+1) * self.m2_pitch,0) - if self.rail_1_names[i] in ["clk_buf", "clk_bar", "vdd", "gnd"]: - self.add_layout_pin(text=self.rail_1_names[i], - layer="metal2", - offset=offset, - width=drc["minwidth_metal2"], - height=control_rail_height) - else: - # just for LVS correspondence... - self.add_label_pin(text=self.rail_1_names[i], - layer="metal2", - offset=offset, - width=drc["minwidth_metal2"], - height=control_rail_height) - self.rail_1_x_offsets[self.rail_1_names[i]]=offset.x + 0.5*drc["minwidth_metal2"] # center offset + # for i in range(self.num_rails_1): + # offset = vector(self.rail_1_start_x + (i+1) * self.m2_pitch,0) + # if self.rail_1_names[i] in ["clk_buf", "clk_bar", "vdd", "gnd"]: + # self.add_layout_pin(text=self.rail_1_names[i], + # layer="metal2", + # offset=offset, + # width=drc["minwidth_metal2"], + # height=control_rail_height) + # else: + # # just for LVS correspondence... + # self.add_label_pin(text=self.rail_1_names[i], + # layer="metal2", + # offset=offset, + # width=drc["minwidth_metal2"], + # height=control_rail_height) + # self.rail_1_x_offsets[self.rail_1_names[i]]=offset.x + 0.5*drc["minwidth_metal2"] # center offset - # pins are in order ["oeb","csb","web"] # 0 1 2 - self.connect_rail_from_left_m2m3(self.msf_inst,"dout_bar[0]","oe") - self.connect_rail_from_left_m2m3(self.msf_inst,"dout[0]","oe_bar") - self.connect_rail_from_left_m2m3(self.msf_inst,"dout_bar[1]","cs") - self.connect_rail_from_left_m2m3(self.msf_inst,"dout_bar[2]","we") + # # # Connect the gnd and vdd of the control + # # gnd_pins = self.msf_inst.get_pins("gnd") + # # for p in gnd_pins: + # # if p.layer != "metal2": + # # continue + # # gnd_pin = p.rc() + # # gnd_rail_position = vector(self.rail_1_x_offsets["gnd"], gnd_pin.y) + # # self.add_wire(("metal3","via2","metal2"),[gnd_pin, gnd_rail_position]) + # # self.add_via_center(layers=("metal2","via2","metal3"), + # # offset=gnd_pin, + # # rotate=90) + # # self.add_via_center(layers=("metal2","via2","metal3"), + # # offset=gnd_rail_position, + # # rotate=90) - # Connect the gnd and vdd of the control - gnd_pins = self.msf_inst.get_pins("gnd") - for p in gnd_pins: - if p.layer != "metal2": - continue - gnd_pin = p.rc() - gnd_rail_position = vector(self.rail_1_x_offsets["gnd"], gnd_pin.y) - self.add_wire(("metal3","via2","metal2"),[gnd_pin, gnd_rail_position]) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=gnd_pin, - rotate=90) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=gnd_rail_position, - rotate=90) + # # vdd_pins = self.msf_inst.get_pins("vdd") + # # for p in vdd_pins: + # # if p.layer != "metal1": + # # continue + # # clk_vdd_position = vector(p.bc().x,self.clk_buf.get_pin("vdd").uy()) + # # self.add_path("metal1",[p.bc(),clk_vdd_position]) - vdd_pins = self.msf_inst.get_pins("vdd") - for p in vdd_pins: - if p.layer != "metal1": - continue - clk_vdd_position = vector(p.bc().x,self.clk_buf.get_pin("vdd").uy()) - self.add_path("metal1",[p.bc(),clk_vdd_position]) - - - def add_rblk_routing(self): @@ -482,8 +508,13 @@ class control_logic(design.design): def add_trien_routing(self): + self.connect_rail_from_right(self.oe_inv,"A","oe") + self.connect_rail_from_right(self.tri_en,"A","clk_buf") - self.connect_rail_from_right(self.tri_en,"B","oe_bar") + oe_inv_out_pos = self.oe_inv.get_pin("Z").ul() + in_pos = self.tri_en.get_pin("B").rc() + mid1 = vector(oe_inv_out_pos.x,in_pos.y) + self.add_path("metal1",[oe_inv_out_pos,mid1,in_pos]) self.connect_rail_from_right_m2m3(self.tri_en_bar,"A","clk_bar") self.connect_rail_from_right_m2m3(self.tri_en_bar,"B","oe") @@ -500,43 +531,43 @@ class control_logic(design.design): self.add_path("metal1",[self.pre_s_en_bar.get_pin("Z").center(), self.s_en.get_pin("A").center()]) - def add_clk_routing(self): - """ Route the clk and clk_bar signal internally """ + # def add_clk_routing(self): + # """ Route the clk and clk_bar signal internally """ - # clk_buf - clk_buf_pos = self.clk_buf.get_pin("Z").rc() - clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], clk_buf_pos.y) - self.add_wire(("metal1","via1","metal2"),[clk_buf_pos, clk_buf_rail_position]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=clk_buf_rail_position, - rotate=90) + # # clk_buf + # clk_buf_pos = self.clk_buf.get_pin("Z").rc() + # clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], clk_buf_pos.y) + # self.add_wire(("metal1","via1","metal2"),[clk_buf_pos, clk_buf_rail_position]) + # self.add_via_center(layers=("metal1","via1","metal2"), + # offset=clk_buf_rail_position, + # rotate=90) - # clk_bar, routes over the clock buffer vdd rail - clk_pin = self.clk_bar.get_pin("Z") - vdd_pin = self.clk_bar.get_pin("vdd") - # move the output pin up to metal2 - self.add_via_center(layers=("metal1","via1","metal2"), - offset=clk_pin.rc(), - rotate=90) - # route to a position over the supply rail - in_pos = vector(clk_pin.rx(), vdd_pin.cy()) - self.add_path("metal2",[clk_pin.rc(), in_pos]) - # connect that position to the control bus - rail_pos = vector(self.rail_1_x_offsets["clk_bar"], in_pos.y) - self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos]) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=in_pos, - rotate=90) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=rail_pos, - rotate=90) + # # clk_bar, routes over the clock buffer vdd rail + # clk_pin = self.clk_bar.get_pin("Z") + # vdd_pin = self.clk_bar.get_pin("vdd") + # # move the output pin up to metal2 + # self.add_via_center(layers=("metal1","via1","metal2"), + # offset=clk_pin.rc(), + # rotate=90) + # # route to a position over the supply rail + # in_pos = vector(clk_pin.rx(), vdd_pin.cy()) + # self.add_path("metal2",[clk_pin.rc(), in_pos]) + # # connect that position to the control bus + # rail_pos = vector(self.rail_1_x_offsets["clk_bar"], in_pos.y) + # self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos]) + # self.add_via_center(layers=("metal2","via2","metal3"), + # offset=in_pos, + # rotate=90) + # self.add_via_center(layers=("metal2","via2","metal3"), + # offset=rail_pos, + # rotate=90) - # clk_buf to msf control flops - msf_clk_pos = self.msf_inst.get_pin("clk").bc() - mid1 = msf_clk_pos - vector(0,self.m2_pitch) - clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], mid1.y) - # route on M2 to allow vdd connection - self.add_wire(("metal2","via1","metal1"),[msf_clk_pos, mid1, clk_buf_rail_position]) + # # clk_buf to msf control flops + # msf_clk_pos = self.msf_inst.get_pin("clk").bc() + # mid1 = msf_clk_pos - vector(0,self.m2_pitch) + # clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], mid1.y) + # # route on M2 to allow vdd connection + # self.add_wire(("metal2","via1","metal1"),[msf_clk_pos, mid1, clk_buf_rail_position]) def connect_right_pin_to_output_pin(self, inst, pin_name, out_name): """ Create an output pin on the bottom side from the pin of a given instance. """ @@ -580,7 +611,7 @@ class control_logic(design.design): well_width = drc["minwidth_well"] # M1 gnd rail from inv1 to max - start_offset = self.clk_inv1.get_pin("gnd").lc() + start_offset = self.clkbuf.get_pin("gnd").lc() row1_gnd_end_offset = vector(rows_end,start_offset.y) self.add_path("metal1",[start_offset,row1_gnd_end_offset]) rail_position = vector(self.rail_1_x_offsets["gnd"], start_offset.y) @@ -597,7 +628,7 @@ class control_logic(design.design): height=well_width) # M1 vdd rail from inv1 to max - start_offset = self.clk_inv1.get_pin("vdd").lc() + start_offset = self.clkbuf.get_pin("vdd").lc() row1_vdd_end_offset = vector(rows_end,start_offset.y) self.add_path("metal1",[start_offset,row1_vdd_end_offset]) rail_position = vector(self.rail_1_x_offsets["vdd"], start_offset.y) @@ -667,19 +698,19 @@ class control_logic(design.design): These should probably be turned off by default though, since extraction will show these as ports in the extracted netlist. """ - pin=self.clk_inv1.get_pin("Z") - self.add_label_pin(text="clk1_bar", - layer="metal1", - offset=pin.ll(), - height=pin.height(), - width=pin.width()) + # pin=self.clk_inv1.get_pin("Z") + # self.add_label_pin(text="clk1_bar", + # layer="metal1", + # offset=pin.ll(), + # height=pin.height(), + # width=pin.width()) - pin=self.clk_inv2.get_pin("Z") - self.add_label_pin(text="clk2", - layer="metal1", - offset=pin.ll(), - height=pin.height(), - width=pin.width()) + # pin=self.clk_inv2.get_pin("Z") + # self.add_label_pin(text="clk2", + # layer="metal1", + # offset=pin.ll(), + # height=pin.height(), + # width=pin.width()) pin=self.rbl.get_pin("out") self.add_label_pin(text="out", @@ -688,4 +719,4 @@ class control_logic(design.design): height=pin.height(), width=pin.width()) - \ No newline at end of file + diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index 894b6dfb..0b2ff335 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -34,7 +34,7 @@ class delay_chain(design.design): self.add_pins() self.create_module() - self.route_inv() + self.route_inverters() self.add_layout_pins() self.DRC_LVS() @@ -48,82 +48,69 @@ class delay_chain(design.design): def create_module(self): """ Add the inverter logical module """ - self.create_inv_list() - self.inv = pinv(route_output=False) self.add_mod(self.inv) # half chain length is the width of the layout # invs are stacked into 2 levels so input/output are close # extra metal is for the gnd connection U - self.width = self.num_top_half * self.inv.width + 2*drc["metal1_to_metal1"] + 0.5*drc["minwidth_metal1"] - self.height = 2 * self.inv.height + self.height = len(self.fanout_list)*self.inv.height + self.width = (max(self.fanout_list)+1) * self.inv.width - self.add_inv_list() + self.add_inverters() - def create_inv_list(self): - """ - Generate a list of inverters. Each inverter has a stage - number and a flag indicating if it is a dummy load. This is - the order that they will get placed too. - """ - # First stage is always 0 and is not a dummy load - self.inv_list=[[0,False]] - for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list): - for i in range(fanout_size-1): - # Add the dummy loads - self.inv_list.append([stage_num+1, True]) - - # Add the gate to drive the next stage - self.inv_list.append([stage_num+1, False]) - def add_inv_list(self): + def add_inverters(self): """ Add the inverters and connect them based on the stage list """ - dummy_load_counter = 1 - self.inv_inst_list = [] - for i in range(self.num_inverters): - # First place the gates - if i < self.num_top_half: - # add top level that is upside down - inv_offset = vector(i * self.inv.width, 2 * self.inv.height) - inv_mirror="MX" + self.driver_inst_list = [] + self.rightest_load_inst = {} + self.load_inst_map = {} + for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list): + if stage_num % 2: + inv_mirror = "MX" + inv_offset = vector(0, (stage_num+1)* self.inv.height) else: - # add bottom level from right to left - inv_offset = vector((self.num_inverters - i) * self.inv.width, 0) - inv_mirror="MY" - - cur_inv=self.add_inst(name="dinv{}".format(i), + inv_mirror = "R0" + inv_offset = vector(0, stage_num * self.inv.height) + + # Add the inverter + cur_driver=self.add_inst(name="dinv{}".format(stage_num), mod=self.inv, offset=inv_offset, mirror=inv_mirror) # keep track of the inverter instances so we can use them to get the pins - self.inv_inst_list.append(cur_inv) + self.driver_inst_list.append(cur_driver) - # Second connect them logically - cur_stage = self.inv_list[i][0] - next_stage = self.inv_list[i][0]+1 - if i == 0: - input = "in" + + # Hook up the driver + if stage_num+1==len(self.fanout_list): + stageout_name = "out" else: - input = "s{}".format(cur_stage) - if i == self.num_inverters-1: - output = "out" - else: - output = "s{}".format(next_stage) - - # if the gate is a dummy load don't connect the output - # else reset the counter - if self.inv_list[i][1]: - output = output+"n{0}".format(dummy_load_counter) - dummy_load_counter += 1 + stageout_name = "dout_{}".format(stage_num+1) + if stage_num == 0: + stagein_name = "in" else: - dummy_load_counter = 1 - - self.connect_inst(args=[input, output, "vdd", "gnd"]) - - if i != 0: - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=cur_inv.get_pin("A").center()) + stagein_name = "dout_{}".format(stage_num) + self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"]) + + # Now add the dummy loads to the right + self.load_inst_map[cur_driver]=[] + for i in range(fanout_size): + inv_offset += vector(self.inv.width,0) + cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num,i), + mod=self.inv, + offset=inv_offset, + mirror=inv_mirror) + # Fanout stage is always driven by driver and output is disconnected + disconnect_name = "n_{0}_{1}".format(stage_num,i) + self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"]) + + # Keep track of all the loads to connect their inputs as a load + self.load_inst_map[cur_driver].append(cur_load) + else: + # Keep track of the last one so we can add the the wire later + self.rightest_load_inst[cur_driver]=cur_load + def add_route(self, pin1, pin2): """ This guarantees that we route from the top to bottom row correctly. """ pin1_pos = pin1.center() @@ -135,79 +122,88 @@ class delay_chain(design.design): # Written this way to guarantee it goes right first if we are switching rows self.add_path("metal2", [pin1_pos, vector(pin1_pos.x,mid_point.y), mid_point, vector(mid_point.x,pin2_pos.y), pin2_pos]) - def route_inv(self): + def route_inverters(self): """ Add metal routing for each of the fanout stages """ - start_inv = end_inv = 0 - for fanout in self.fanout_list: - # end inv number depends on the fan out number - end_inv = start_inv + fanout - start_inv_inst = self.inv_inst_list[start_inv] - - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=start_inv_inst.get_pin("Z").center()), - # route from output to first load - start_inv_pin = start_inv_inst.get_pin("Z") - load_inst = self.inv_inst_list[start_inv+1] - load_pin = load_inst.get_pin("A") - self.add_route(start_inv_pin, load_pin) - - next_inv = start_inv+2 - while next_inv <= end_inv: - prev_load_inst = self.inv_inst_list[next_inv-1] - prev_load_pin = prev_load_inst.get_pin("A") - load_inst = self.inv_inst_list[next_inv] - load_pin = load_inst.get_pin("A") - self.add_route(prev_load_pin, load_pin) - next_inv += 1 - # set the start of next one after current end - start_inv = end_inv + for i in range(len(self.driver_inst_list)): + inv = self.driver_inst_list[i] + for load in self.load_inst_map[inv]: + # Drop a via on each A pin + a_pin = load.get_pin("A") + self.add_via_center(layers=("metal1","via1","metal2"), + offset=a_pin.center(), + rotate=90) + self.add_via_center(layers=("metal2","via2","metal3"), + offset=a_pin.center(), + rotate=90) + # Route an M3 horizontal wire to the furthest + z_pin = inv.get_pin("Z") + a_pin = inv.get_pin("A") + a_max = self.rightest_load_inst[inv].get_pin("A") + self.add_via_center(layers=("metal1","via1","metal2"), + offset=a_pin.center(), + rotate=90) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=z_pin.center(), + rotate=90) + self.add_via_center(layers=("metal2","via2","metal3"), + offset=z_pin.center(), + rotate=90) + self.add_path("metal3",[z_pin.center(), a_max.center()]) + + + # Route Z to the A of the next stage + if i+1 < len(self.driver_inst_list): + z_pin = inv.get_pin("Z") + next_inv = self.driver_inst_list[i+1] + next_a_pin = next_inv.get_pin("A") + y_mid = (z_pin.cy() + next_a_pin.cy())/2 + mid1_point = vector(z_pin.cx(), y_mid) + mid2_point = vector(next_a_pin.cx(), y_mid) + self.add_path("metal2",[z_pin.center(), mid1_point, mid2_point, next_a_pin.center()]) + + def add_layout_pins(self): """ Add vdd and gnd rails and the input/output. Connect the gnd rails internally on the top end with no input/output to obstruct. """ - vdd_pin = self.inv.get_pin("vdd") - gnd_pin = self.inv.get_pin("gnd") - for i in range(3): - (offset,y_dir)=self.get_gate_offset(0, self.inv.height, i) - rail_width = self.num_top_half * self.inv.width - if i % 2: - self.add_layout_pin(text="vdd", - layer="metal1", - offset=offset + vdd_pin.ll().scale(1,y_dir), - width=rail_width, - height=drc["minwidth_metal1"]) - else: - self.add_layout_pin(text="gnd", - layer="metal1", - offset=offset + gnd_pin.ll().scale(1,y_dir), - width=rail_width, - height=drc["minwidth_metal1"]) - # Use the right most parts of the gnd rails and add a U connector - # We still have the two gnd pins, but it is an either-or connect - gnd_pins = self.get_pins("gnd") - gnd_start = gnd_pins[0].rc() - gnd_mid1 = gnd_start + vector(2*drc["metal1_to_metal1"],0) - gnd_end = gnd_pins[1].rc() - gnd_mid2 = gnd_end + vector(2*drc["metal1_to_metal1"],0) - #self.add_wire(("metal1","via1","metal2"), [gnd_start, gnd_mid1, gnd_mid2, gnd_end]) - self.add_path("metal1", [gnd_start, gnd_mid1, gnd_mid2, gnd_end]) - + for driver in self.driver_inst_list: + vdd_pin = driver.get_pin("vdd") + self.add_layout_pin(text="vdd", + layer="metal1", + offset=vdd_pin.ll(), + width=self.width, + height=vdd_pin.height()) + gnd_pin = driver.get_pin("gnd") + self.add_layout_pin(text="gnd", + layer="metal1", + offset=gnd_pin.ll(), + width=self.width, + height=gnd_pin.height()) + # input is A pin of first inverter - a_pin = self.inv_inst_list[0].get_pin("A") + a_pin = self.driver_inst_list[0].get_pin("A") + self.add_via_center(layers=("metal1","via1","metal2"), + offset=a_pin.center(), + rotate=90) self.add_layout_pin(text="in", - layer="metal1", - offset=a_pin.ll(), + layer="metal2", + offset=a_pin.ll().scale(1,0), width=a_pin.width(), - height=a_pin.height()) + height=a_pin.cy()) + - - # output is Z pin of last inverter - z_pin = self.inv_inst_list[-1].get_pin("Z") + # output is A pin of last load inverter + last_driver_inst = self.driver_inst_list[-1] + a_pin = self.rightest_load_inst[last_driver_inst].get_pin("A") + self.add_via_center(layers=("metal1","via1","metal2"), + offset=a_pin.center(), + rotate=90) + mid_point = vector(a_pin.cx()+3*self.m2_width,a_pin.cy()) + self.add_path("metal2",[a_pin.center(), mid_point, mid_point.scale(1,0)]) self.add_layout_pin(text="out", - layer="metal1", - offset=z_pin.ll().scale(0,1), - width=z_pin.lx()) + layer="metal2", + offset=mid_point.scale(1,0)) diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 293044b2..1e414f88 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -4,7 +4,6 @@ from tech import drc from math import log from vector import vector from globals import OPTS -import dff_buf class dff_array(design.design): """ @@ -21,7 +20,9 @@ class dff_array(design.design): design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) - self.dff = dff_buf.dff_buf(inv1_size, inv2_size) + c = reload(__import__(OPTS.dff)) + self.mod_dff = getattr(c, OPTS.dff) + self.dff = self.mod_dff("dff") self.add_mod(self.dff) self.width = self.columns * self.dff.width @@ -42,7 +43,6 @@ class dff_array(design.design): for y in range(self.rows): for x in range(self.columns): self.add_pin(self.get_dout_name(y,x)) - self.add_pin(self.get_dout_bar_name(y,x)) self.add_pin("clk") self.add_pin("vdd") self.add_pin("gnd") @@ -64,7 +64,6 @@ class dff_array(design.design): mirror=mirror) self.connect_inst([self.get_din_name(y,x), self.get_dout_name(y,x), - self.get_dout_bar_name(y,x), "clk", "vdd", "gnd"]) @@ -89,15 +88,6 @@ class dff_array(design.design): return dout_name - def get_dout_bar_name(self, row, col): - if self.columns == 1: - dout_bar_name = "dout_bar[{0}]".format(row) - elif self.rows == 1: - dout_bar_name = "dout_bar[{0}]".format(col) - else: - dout_bar_name = "dout_bar[{0}][{1}]".format(row,col) - - return dout_bar_name def add_layout_pins(self): @@ -138,14 +128,6 @@ class dff_array(design.design): height=dout_pin.height()) - dout_bar_pin = self.dff_insts[x,y].get_pin("Qb") - debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2") - self.add_layout_pin(text=self.get_dout_bar_name(y,x), - layer=dout_bar_pin.layer, - offset=dout_bar_pin.ll(), - width=dout_bar_pin.width(), - height=dout_bar_pin.height()) - # Create vertical spines to a single horizontal rail clk_pin = self.dff_insts[0,0].get_pin("clk") diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py new file mode 100644 index 00000000..24688f68 --- /dev/null +++ b/compiler/modules/dff_buf_array.py @@ -0,0 +1,180 @@ +import debug +import design +from tech import drc +from math import log +from vector import vector +from globals import OPTS +import dff_buf + +class dff_buf_array(design.design): + """ + This is a simple row (or multiple rows) of flops. + Unlike the data flops, these are never spaced out. + """ + + def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""): + self.rows = rows + self.columns = columns + + if name=="": + name = "dff_array_{0}x{1}".format(rows, columns) + design.design.__init__(self, name) + debug.info(1, "Creating {}".format(self.name)) + + self.dff = dff_buf.dff_buf(inv1_size, inv2_size) + self.add_mod(self.dff) + + self.width = self.columns * self.dff.width + self.height = self.rows * self.dff.height + + self.create_layout() + + def create_layout(self): + self.add_pins() + self.create_dff_array() + self.add_layout_pins() + self.DRC_LVS() + + def add_pins(self): + for y in range(self.rows): + for x in range(self.columns): + self.add_pin(self.get_din_name(y,x)) + for y in range(self.rows): + for x in range(self.columns): + self.add_pin(self.get_dout_name(y,x)) + self.add_pin(self.get_dout_bar_name(y,x)) + self.add_pin("clk") + self.add_pin("vdd") + self.add_pin("gnd") + + def create_dff_array(self): + self.dff_insts={} + for y in range(self.rows): + for x in range(self.columns): + name = "Xdff_r{0}_c{1}".format(y,x) + if (y % 2 == 0): + base = vector(x*self.dff.width,y*self.dff.height) + mirror = "R0" + else: + base = vector(x*self.dff.width,(y+1)*self.dff.height) + mirror = "MX" + self.dff_insts[x,y]=self.add_inst(name=name, + mod=self.dff, + offset=base, + mirror=mirror) + self.connect_inst([self.get_din_name(y,x), + self.get_dout_name(y,x), + self.get_dout_bar_name(y,x), + "clk", + "vdd", + "gnd"]) + + def get_din_name(self, row, col): + if self.columns == 1: + din_name = "din[{0}]".format(row) + elif self.rows == 1: + din_name = "din[{0}]".format(col) + else: + din_name = "din[{0}][{1}]".format(row,col) + + return din_name + + def get_dout_name(self, row, col): + if self.columns == 1: + dout_name = "dout[{0}]".format(row) + elif self.rows == 1: + dout_name = "dout[{0}]".format(col) + else: + dout_name = "dout[{0}][{1}]".format(row,col) + + return dout_name + + def get_dout_bar_name(self, row, col): + if self.columns == 1: + dout_bar_name = "dout_bar[{0}]".format(row) + elif self.rows == 1: + dout_bar_name = "dout_bar[{0}]".format(col) + else: + dout_bar_name = "dout_bar[{0}][{1}]".format(row,col) + + return dout_bar_name + + def add_layout_pins(self): + + for y in range(self.rows): + # Continous vdd rail along with label. + vdd_pin=self.dff_insts[0,y].get_pin("vdd") + self.add_layout_pin(text="vdd", + layer="metal1", + offset=vdd_pin.ll(), + width=self.width, + height=self.m1_width) + + # Continous gnd rail along with label. + gnd_pin=self.dff_insts[0,y].get_pin("gnd") + self.add_layout_pin(text="gnd", + layer="metal1", + offset=gnd_pin.ll(), + width=self.width, + height=self.m1_width) + + + for y in range(self.rows): + for x in range(self.columns): + din_pin = self.dff_insts[x,y].get_pin("D") + debug.check(din_pin.layer=="metal2","DFF D pin not on metal2") + self.add_layout_pin(text=self.get_din_name(y,x), + layer=din_pin.layer, + offset=din_pin.ll(), + width=din_pin.width(), + height=din_pin.height()) + + dout_pin = self.dff_insts[x,y].get_pin("Q") + debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2") + self.add_layout_pin(text=self.get_dout_name(y,x), + layer=dout_pin.layer, + offset=dout_pin.ll(), + width=dout_pin.width(), + height=dout_pin.height()) + + + dout_bar_pin = self.dff_insts[x,y].get_pin("Qb") + debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2") + self.add_layout_pin(text=self.get_dout_bar_name(y,x), + layer=dout_bar_pin.layer, + offset=dout_bar_pin.ll(), + width=dout_bar_pin.width(), + height=dout_bar_pin.height()) + + + # Create vertical spines to a single horizontal rail + clk_pin = self.dff_insts[0,0].get_pin("clk") + debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2") + if self.columns==1: + self.add_layout_pin(text="clk", + layer="metal2", + offset=clk_pin.ll().scale(1,0), + width=self.m2_width, + height=self.height) + else: + self.add_layout_pin(text="clk", + layer="metal3", + offset=vector(0,0), + width=self.width, + height=self.m3_width) + for x in range(self.columns): + clk_pin = self.dff_insts[x,0].get_pin("clk") + # Make a vertical strip for each column + self.add_layout_pin(text="clk", + layer="metal2", + offset=clk_pin.ll().scale(1,0), + width=self.m2_width, + height=self.height) + # Drop a via to the M3 pin + self.add_via_center(layers=("metal2","via2","metal3"), + offset=clk_pin.center().scale(1,0)) + + + + def analytical_delay(self, slew, load=0.0): + return self.dff.analytical_delay(slew=slew, load=load) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 12c12007..6208b563 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -31,7 +31,7 @@ class hierarchical_decoder(design.design): self.rows = rows self.num_inputs = int(math.log(self.rows, 2)) (self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) - + self.create_layout() self.DRC_LVS() @@ -51,10 +51,15 @@ class hierarchical_decoder(design.design): self.add_mod(self.nand2) self.nand3 = pnand3() self.add_mod(self.nand3) + + self.add_decoders() - # CREATION OF PRE-DECODER + def add_decoders(self): + """ Create the decoders based on the number of pre-decodes """ + # FIXME: Only add these if needed? self.pre2_4 = pre2x4() self.add_mod(self.pre2_4) + self.pre3_8 = pre3x8() self.add_mod(self.pre3_8) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 7e336405..4ace0055 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -21,8 +21,6 @@ class hierarchical_predecode(design.design): c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell_height = self.mod_bitcell.height - def add_pins(self): for k in range(self.number_of_inputs): diff --git a/compiler/options.py b/compiler/options.py index def59c7c..6f564f73 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -13,8 +13,8 @@ class options(optparse.Values): # This is the name of the technology. tech_name = "" # This is the temp directory where all intermediate results are stored. - openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),os.getpid()) - #openram_temp = "/Users/{}/openram_temp/".format(getpass.getuser()) + #openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),os.getpid()) + openram_temp = "/Users/{}/openram_temp/".format(getpass.getuser()) # This is the verbosity level to control debug information. 0 is none, 1 # is minimal, etc. debug_level = 0 @@ -59,7 +59,7 @@ class options(optparse.Values): ms_flop = "ms_flop" ms_flop_array = "ms_flop_array" dff = "dff" - dff_array = "dff_array" + dff_array = "dff_buf_array" control_logic = "control_logic" bitcell_array = "bitcell_array" sense_amp = "sense_amp" diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py new file mode 100644 index 00000000..2e0cc20b --- /dev/null +++ b/compiler/pgates/pinvbuf.py @@ -0,0 +1,159 @@ +import debug +import design +from tech import drc +from math import log +from vector import vector +from globals import OPTS +from pinv import pinv + +class pinvbuf(design.design): + """ + This is a simple inverter/buffer used for driving loads. It is + used in the column decoder for 1:2 decoding and as the clock buffer. + """ + + def __init__(self, inv1_size=2, inv2_size=4, name=""): + + if name=="": + name = "pinvbuf_{0}_{1}".format(inv1_size, inv2_size) + design.design.__init__(self, name) + debug.info(1, "Creating {}".format(self.name)) + + self.inv = pinv(size=1) + self.add_mod(self.inv) + + self.inv1 = pinv(size=inv1_size) + self.add_mod(self.inv1) + + self.inv2 = pinv(size=inv2_size) + self.add_mod(self.inv2) + + self.width = self.inv1.width + self.inv2.width + self.height = 2*self.inv1.height + + self.create_layout() + + self.offset_all_coordinates() + + self.DRC_LVS() + + def create_layout(self): + self.add_pins() + self.add_insts() + self.add_wires() + self.add_layout_pins() + + def add_pins(self): + self.add_pin("A") + self.add_pin("Zb") + self.add_pin("Z") + self.add_pin("vdd") + self.add_pin("gnd") + + def add_insts(self): + # Add INV1 to the right (capacitance shield) + self.inv1_inst=self.add_inst(name="buf_inv1", + mod=self.inv, + offset=vector(0,0)) + self.connect_inst(["A", "zb_int", "vdd", "gnd"]) + + + # Add INV2 to the right + self.inv2_inst=self.add_inst(name="buf_inv2", + mod=self.inv1, + offset=vector(self.inv1_inst.rx(),0)) + self.connect_inst(["zb_int", "z_int", "vdd", "gnd"]) + + # Add INV3 to the right + self.inv3_inst=self.add_inst(name="buf_inv3", + mod=self.inv2, + offset=vector(self.inv2_inst.rx(),0)) + self.connect_inst(["z_int", "Zb", "vdd", "gnd"]) + + # Add INV4 to the bottom + self.inv4_inst=self.add_inst(name="buf_inv4", + mod=self.inv2, + offset=vector(self.inv2_inst.rx(),2*self.inv2.height), + mirror = "MX") + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + + 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_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()]) + + # inv2 Z to inv3 A + z2_pin = self.inv2_inst.get_pin("Z") + a3_pin = self.inv3_inst.get_pin("A") + mid_point = vector(z2_pin.cx(), a3_pin.cy()) + self.add_path("metal1", [z2_pin.center(), mid_point, a3_pin.center()]) + + # inv1 Z to inv4 A (up and over) + z1_pin = self.inv1_inst.get_pin("Z") + a4_pin = self.inv4_inst.get_pin("A") + mid_point = vector(z1_pin.cx(), a4_pin.cy()) + self.add_wire(("metal1","via1","metal2"), [z1_pin.center(), mid_point, a4_pin.center()]) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=z1_pin.center()) + + + + def add_layout_pins(self): + + # Continous vdd rail along with label. + vdd_pin=self.inv1_inst.get_pin("vdd") + self.add_layout_pin(text="vdd", + layer="metal1", + offset=vdd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + # Continous vdd rail along with label. + gnd_pin=self.inv4_inst.get_pin("gnd") + self.add_layout_pin(text="gnd", + layer="metal1", + offset=gnd_pin.ll().scale(0,1), + width=self.width, + height=gnd_pin.height()) + + # Continous gnd rail along with label. + gnd_pin=self.inv1_inst.get_pin("gnd") + self.add_layout_pin(text="gnd", + layer="metal1", + offset=gnd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + z_pin = self.inv4_inst.get_pin("Z") + self.add_layout_pin_center_rect(text="Z", + layer="metal2", + offset=z_pin.center()) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=z_pin.center()) + + zb_pin = self.inv3_inst.get_pin("Z") + self.add_layout_pin_center_rect(text="Zb", + layer="metal2", + offset=zb_pin.center()) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=zb_pin.center()) + + + a_pin = self.inv1_inst.get_pin("A") + self.add_layout_pin_center_rect(text="A", + layer="metal2", + offset=a_pin.center()) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=a_pin.center()) + + + + def analytical_delay(self, slew, load=0.0): + """ Calculate the analytical delay of DFF-> INV -> INV """ + inv1_delay = self.inv1.analytical_delay(slew=slew, load=self.inv2.input_load()) + inv2_delay = self.inv2.analytical_delay(slew=inv1_delay.slew, load=load) + return inv1_delay + inv2_delay + diff --git a/compiler/sram.py b/compiler/sram.py index 25ef8332..7e28f18e 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -22,8 +22,8 @@ class sram(design.design): c = reload(__import__(OPTS.control_logic)) self.mod_control_logic = getattr(c, OPTS.control_logic) - c = reload(__import__(OPTS.ms_flop_array)) - self.mod_ms_flop_array = getattr(c, OPTS.ms_flop_array) + c = reload(__import__(OPTS.dff_array)) + self.mod_dff_array = getattr(c, OPTS.dff_array) c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) @@ -146,10 +146,10 @@ class sram(design.design): self.add_pin("ADDR[{0}]".format(i),"INPUT") # These are used to create the physical pins too - self.control_logic_inputs=["CSb", "WEb", "OEb", "clk"] + self.control_logic_inputs=["CSb", "WEb", "OEb"] self.control_logic_outputs=["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar", "clk_buf"] - self.add_pin_list(self.control_logic_inputs,"INPUT") + self.add_pin_list(self.control_logic_inputs + ["clk"],"INPUT") self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") @@ -210,7 +210,7 @@ class sram(design.design): """ Route the shared signals for two and four bank configurations. """ # create the input control pins - for n in self.control_logic_inputs: + for n in self.control_logic_inputs + ["clk"]: self.copy_layout_pin(self.control_logic_inst, n.lower(), n) # connect the control logic to the control bus @@ -374,7 +374,7 @@ class sram(design.design): length=self.vertical_bus_height, vertical=True) - self.addr_bus_names=["ADDR[{}]".format(i) for i in range(self.addr_size)] + self.addr_bus_names=["A[{}]".format(i) for i in range(self.addr_size)] self.vert_control_bus_positions.update(self.create_bus(layer="metal2", pitch=self.m2_pitch, offset=self.addr_bus_offset, @@ -797,6 +797,11 @@ class sram(design.design): self.control_logic = self.mod_control_logic(num_rows=self.num_rows) self.add_mod(self.control_logic) + # Create the address and control flops + dff_size = self.addr_size + len(self.control_logic_inputs) + self.addr_ctrl_dff = self.mod_dff_array(rows=dff_size, columns=1) + self.add_mod(self.addr_ctrl_dff) + # Create the bank module (up to four are instantiated) self.bank = bank(word_size=self.word_size, num_words=self.num_words_per_bank, @@ -805,14 +810,13 @@ class sram(design.design): name="bank") self.add_mod(self.bank) - # Conditionally create the + # Create bank decoder if(self.num_banks > 1): self.create_multi_bank_modules() self.bank_count = 0 self.supply_rail_width = self.bank.supply_rail_width - # Leave some extra space for the pitch self.supply_rail_pitch = self.bank.supply_rail_pitch @@ -900,13 +904,32 @@ class sram(design.design): return line_positions + def add_control_addr_dff(self, position, rotate=0): + """ Add and place address and control flops """ + self.addr_ctrl_dff_inst = self.add_inst(name="address", + mod=self.addr_ctrl_dff, + offset=position, + rotate=rotate) + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for i in range(self.addr_size): + inputs.append("ADDR[{}]".format(i)) + outputs.append("A[{}]".format(i)) + + for i in self.control_logic_inputs: + inputs.append(i) + outputs.append(i+"_s") + + self.connect_inst(inputs + outputs + ["clk", "vdd", "gnd"]) + def add_control_logic(self, position, rotate): """ Add and place control logic """ self.control_logic_inst=self.add_inst(name="control", mod=self.control_logic, offset=position, rotate=rotate) - self.connect_inst(self.control_logic_inputs + self.control_logic_outputs + ["vdd", "gnd"]) + self.connect_inst(self.control_logic_inputs + ["clk"] + self.control_logic_outputs + ["vdd", "gnd"]) def add_lvs_correspondence_points(self): @@ -938,11 +961,14 @@ class sram(design.design): # are not recomputed using instance placement. So, place the control logic such that it aligns # with the top of the SRAM. control_gap = 2*self.m3_width - pos = vector(-control_gap, - self.bank.height-self.control_logic.width) - self.add_control_logic(position=pos, - rotate=90) + control_pos = vector(-control_gap, + self.bank.height-self.control_logic.width) + self.add_control_logic(position=control_pos, rotate=90) + addr_pos = vector(self.control_logic_inst.lx(), + 2*self.supply_rail_pitch) + self.add_control_addr_dff(addr_pos) + self.width = self.bank.width + self.control_logic.height + control_gap self.height = self.bank.height @@ -955,7 +981,7 @@ class sram(design.design): self.copy_layout_pin(self.bank_inst, "DATA[{}]".format(i)) for i in range(self.addr_size): - self.copy_layout_pin(self.bank_inst, "ADDR[{}]".format(i)) + self.copy_layout_pin(self.bank_inst, "A[{}]".format(i)) for (old,new) in zip(["csb","web","oeb","clk"],["CSb","WEb","OEb","clk"]): self.copy_layout_pin(self.control_logic_inst, old, new) diff --git a/compiler/tests/04_pinvbuf_test.py b/compiler/tests/04_pinvbuf_test.py new file mode 100644 index 00000000..91ac8998 --- /dev/null +++ b/compiler/tests/04_pinvbuf_test.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a 2-row buffer cell +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class pinvbuf_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + global verify + import verify + OPTS.check_lvsdrc = False + + import pinvbuf + + debug.info(2, "Testing inverter/buffer 4x 8x") + a = pinvbuf.pinvbuf(4,8) + self.local_check(a) + + OPTS.check_lvsdrc = True + 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() diff --git a/compiler/tests/11_dff_buf_array_test.py b/compiler/tests/11_dff_buf_array_test.py new file mode 100644 index 00000000..e4856427 --- /dev/null +++ b/compiler/tests/11_dff_buf_array_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a dff_array. +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class dff_buf_array_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + global verify + import verify + OPTS.check_lvsdrc = False + + import dff_buf_array + + debug.info(2, "Testing dff_buf_array for 3x3") + a = dff_buf_array.dff_buf_array(rows=3, columns=3) + self.local_check(a) + + debug.info(2, "Testing dff_buf_array for 1x3") + a = dff_buf_array.dff_buf_array(rows=1, columns=3) + self.local_check(a) + + debug.info(2, "Testing dff_buf_array for 3x1") + a = dff_buf_array.dff_buf_array(rows=3, columns=1) + self.local_check(a) + + OPTS.check_lvsdrc = True + 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() diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py index 5eb9af3f..6b3d51ed 100644 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_test.py @@ -34,9 +34,9 @@ class single_bank_test(openram_test): self.local_check(a) # Eight way has a short circuit of one column mux select to gnd rail - # debug.info(1, "Eight way column mux") - # a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4") - # self.local_check(a) + debug.info(1, "Eight way column mux") + a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4") + self.local_check(a) OPTS.check_lvsdrc = True globals.end_openram()