From 1f81b24e96241dd7349843f79b8eae83cc68ba30 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 16 Mar 2018 17:46:29 -0700 Subject: [PATCH] Single bank passing DRC and LVS again. Unfold hierarchical decoder to improve routability. --- compiler/base/hierarchy_layout.py | 2 +- compiler/modules/bank.py | 68 +++++---------- compiler/modules/bank_select.py | 16 ++-- compiler/modules/control_logic.py | 22 ++++- compiler/modules/hierarchical_decoder.py | 51 +++++++----- compiler/modules/hierarchical_predecode.py | 14 ++-- compiler/sram.py | 97 ++++++++++++++++------ compiler/verify/magic.py | 1 + technology/freepdk45/tech/tech.py | 4 +- technology/scn3me_subm/tech/tech.py | 5 +- 10 files changed, 163 insertions(+), 117 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index e75f1be3..7ca86af9 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -178,7 +178,7 @@ class layout(lef.lef): """ Return the pin or list of pins """ try: if len(self.pin_map[text])>1: - debug.warning("Should use a pin iterator since more than one pin {}".format(text)) + debug.error("Should use a pin iterator since more than one pin {}".format(text),-1) # If we have one pin, return it and not the list. # Otherwise, should use get_pins() return self.pin_map[text][0] diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index ab3601e8..9528f510 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -96,7 +96,6 @@ class bank(design.design): self.route_tri_gate_out() self.route_wordline_driver() self.route_row_decoder() - self.route_address() self.route_column_address_lines() self.route_control_lines() self.add_control_pins() @@ -174,11 +173,6 @@ class bank(design.design): # 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) - - - @@ -411,7 +405,7 @@ class bank(design.design): """ # 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) + y_off = -(self.row_decoder.predecoder_height + self.col_decoder.height + 2*drc["well_to_well"]) self.col_decoder_inst=self.add_inst(name="col_address_decoder", mod=self.col_decoder, offset=vector(x_off,y_off)) @@ -460,10 +454,8 @@ class bank(design.design): offset=self.bank_select_pos) temp = [] - for i in range(self.num_control_lines): - temp.append(self.input_control_signals[i]) - for i in range(self.num_control_lines): - temp.append(self.control_signals[i]) + temp.extend(self.input_control_signals) + temp.extend(self.control_signals) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -475,7 +467,7 @@ class bank(design.design): layer="metal3", start=vector(self.left_gnd_x_offset,in_pos.y), end=in_pos) - + 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() @@ -505,9 +497,7 @@ class bank(design.design): if self.col_addr_size > 0: col_decoder_min_point = self.col_decoder_inst.by() else: - col_decoder_min_point = row_decoder_min_point - self.addr_bus_height - - self.addr_min_point = row_decoder_min_point - self.addr_bus_height + col_decoder_min_point = row_decoder_min_point if self.num_banks>1: # The control gating logic is below the decoder @@ -575,11 +565,11 @@ class bank(design.design): # 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, + self.add_layout_pin(text=name, layer="metal2", - offset=vector(x_offset, self.addr_min_point), + offset=vector(x_offset, self.min_point), width=self.m2_width, - height=-self.addr_min_point) + height=-self.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 @@ -775,11 +765,11 @@ class bank(design.design): # 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()) + pin_pos = vector(decode_in_pin.cx(), self.min_point) self.add_layout_pin_center_segment(text="A[0]", - layer="metal1", + layer="metal2", start=pin_pos, - end=decode_in_pin.lc()) + end=decode_in_pin.bc()) elif self.col_addr_size > 1: # Route the col decoder outputs to the col select bus @@ -794,14 +784,14 @@ class bank(design.design): # 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=addr_pos, - rotate=90) + decoder_name = "in[{}]".format(i) + addr_name = "A[{}]".format(i) + decode_in_pin = self.col_decoder_inst.get_pin(decoder_name) + pin_pos = vector(decode_in_pin.cx(), self.min_point) + self.add_layout_pin_center_segment(text=addr_name, + layer="metal2", + start=pin_pos, + end=decode_in_pin.bc()) # route the gnd rails, add contact to rail as well @@ -823,26 +813,6 @@ class bank(design.design): rotate=90) - def route_address(self): - """ Routing the row address lines from the address ms-flop array to the row-decoder """ - - - for i in range(self.addr_size): - 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) def add_lvs_correspondence_points(self): diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index de79f08b..31aa0a8a 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -21,18 +21,14 @@ class bank_select(design.design): # Number of control lines in the bus 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"] + self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_buf_bar", "w_en", "s_en"] # These will be outputs of the gaters if this is multibank self.control_signals = ["gated_"+str for str in self.input_control_signals] - - for i in range(self.num_control_lines): - input_name = self.input_control_signals[i] - self.add_pin(input_name) - for i in range(self.num_control_lines): - gated_name = self.control_signals[i] - self.add_pin(gated_name) - self.add_pin("vdd") - self.add_pin("gnd") + + self.add_pin_list(self.input_control_signals, "INPUT") + self.add_pin_list(self.control_signals, "OUTPUT") + self.add_pin("vdd","POWER") + self.add_pin("gnd","GROUND") self.create_modules() self.calculate_module_offsets() diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 3e52a6f3..4d6703e4 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -82,7 +82,7 @@ class control_logic(design.design): # Some cells may have pwell/nwell spacing problems too when the wells are different heights. #self.cell_gap = max(self.m2_pitch,drc["pwell_to_nwell"]) - self.input_list =["web","csb","oeb"] + self.input_list =["csb","web","oeb"] self.input_width = len(self.input_list)*self.m2_pitch self.input_bar_list = ["clk_buf_bar", "we", "cs", "oe"] self.input_bar_width = len(self.input_bar_list)*self.m2_pitch @@ -602,7 +602,7 @@ class control_logic(design.design): rows_end = self.width well_width = drc["minwidth_well"] - for i in range(7): + for i in range(8): if i%2: name = "vdd" well_type = "nwell" @@ -648,6 +648,24 @@ class control_logic(design.design): start=left, end=right) + for vdd_pin in self.rbl_inst.get_pins("vdd"): + if vdd_pin.layer != "metal2": + continue + self.add_layout_pin(text="vdd", + layer="metal2", + offset=vdd_pin.ll(), + height=vdd_pin.height(), + width=vdd_pin.width()) + + for gnd_pin in self.rbl_inst.get_pins("gnd"): + if gnd_pin.layer != "metal2": + continue + self.add_layout_pin(text="gnd", + layer="metal2", + offset=gnd_pin.ll(), + height=gnd_pin.height(), + width=gnd_pin.width()) + def add_lvs_correspondence_points(self): """ This adds some points for easier debugging if LVS goes wrong. diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 6208b563..ac36359a 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -33,6 +33,9 @@ class hierarchical_decoder(design.design): (self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) self.create_layout() + + self.offset_all_coordinates() + self.DRC_LVS() def create_layout(self): @@ -155,8 +158,8 @@ class hierarchical_decoder(design.design): self.row_decoder_height = self.inv.height * self.rows # Calculates height and width of hierarchical decoder - self.height = self.predecoder_height + self.row_decoder_height - self.width = self.predecoder_width + self.routing_width + self.height = self.row_decoder_height + self.width = self.predecoder_width + self.row_decoder_width def create_pre_decoder(self): """ Creates pre-decoder and places labels input address [A] """ @@ -171,12 +174,10 @@ class hierarchical_decoder(design.design): """ Add a 2x4 predecoder """ if (self.num_inputs == 2): - base = vector(self.routing_width,0) - mirror = "RO" + base = vector(-self.pre2_4.width,0) index_off1 = index_off2 = 0 else: - base= vector(self.routing_width+self.pre2_4.width, num * self.pre2_4.height) - mirror = "MY" + base= vector(-self.pre2_4.width, num * self.pre2_4.height) index_off1 = num * 2 index_off2 = num * 4 @@ -189,8 +190,7 @@ class hierarchical_decoder(design.design): self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num), mod=self.pre2_4, - offset=base, - mirror=mirror)) + offset=base)) self.connect_inst(pins) self.add_pre2x4_pins(num) @@ -215,12 +215,11 @@ class hierarchical_decoder(design.design): def add_pre3x8(self,num): """ Add 3x8 numbered predecoder """ if (self.num_inputs == 3): - offset = vector(self.routing_width,0) + offset = vector(-self.pre_3_8.width,0) mirror ="R0" else: height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height - offset = vector(self.routing_width+self.pre3_8.width, height) - mirror="MY" + offset = vector(-self.pre3_8.width, height) # If we had 2x4 predecodes, those are used as the lower # decode output bits @@ -236,8 +235,7 @@ class hierarchical_decoder(design.design): self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num), mod=self.pre3_8, - offset=offset, - mirror=mirror)) + offset=offset)) self.connect_inst(pins) # The 3x8 predecoders will be stacked, so use yoffset @@ -305,11 +303,11 @@ class hierarchical_decoder(design.design): for row in range(self.rows): name = "DEC_NAND[{0}]".format(row) if ((row % 2) == 0): - y_off = self.predecoder_height + nand_mod.height*row + y_off = nand_mod.height*row y_dir = 1 mirror = "R0" else: - y_off = self.predecoder_height + nand_mod.height*(row + 1) + y_off = nand_mod.height*(row + 1) y_dir = -1 mirror = "MX" @@ -342,7 +340,7 @@ class hierarchical_decoder(design.design): inv_row_height = self.inv.height * (row + 1) mirror = "MX" y_dir = -1 - y_off = self.predecoder_height + inv_row_height + y_off = inv_row_height offset = vector(x_off,y_off) self.inv_inst.append(self.add_inst(name=name, @@ -408,7 +406,7 @@ class hierarchical_decoder(design.design): index = pre_num * 4 + i out_name = "out[{}]".format(i) pin = self.pre2x4_inst[pre_num].get_pin(out_name) - self.connect_rail(index, pin) + self.connect_rail_m3(index, pin) for pre_num in range(self.no_of_pre3x8): @@ -416,7 +414,7 @@ class hierarchical_decoder(design.design): index = pre_num * 8 + i + self.no_of_pre2x4 * 4 out_name = "out[{}]".format(i) pin = self.pre3x8_inst[pre_num].get_pin(out_name) - self.connect_rail(index, pin) + self.connect_rail_m3(index, pin) @@ -448,11 +446,11 @@ class hierarchical_decoder(design.design): def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ - for num in range(0,self.total_number_of_predecoder_outputs + self.rows): + for num in range(0,self.rows): # this will result in duplicate polygons for rails, but who cares # use the inverter offset even though it will be the nand's too - (gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num) + (gate_offset, y_dir) = self.get_gate_offset(-self.predecoder_width, self.inv.height, num) # route vdd vdd_offset = gate_offset + self.inv.get_pin("vdd").ll().scale(1,y_dir) self.add_layout_pin(text="vdd", @@ -478,6 +476,19 @@ class hierarchical_decoder(design.design): offset=rail_pos, rotate=90) + + def connect_rail_m3(self, rail_index, pin): + """ Connect the routing rail to the given metal1 pin """ + mid_point = vector(pin.cx(), pin.cy()-self.inv.height/2) + rail_pos = vector(self.rail_x_offsets[rail_index],mid_point.y) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=pin.center(), + rotate=90) + self.add_wire(("metal3","via2","metal2"), [rail_pos, mid_point, pin.uc()]) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=rail_pos, + rotate=90) + def analytical_delay(self, slew, load = 0.0): # A -> out diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 4ace0055..4e5ede5e 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -49,9 +49,8 @@ class hierarchical_predecode(design.design): debug.error("Invalid number of predecode inputs.",-1) def setup_constraints(self): - # we are going to use horizontal vias, so use the via height # use a conservative douple spacing just to get rid of annoying via DRCs - self.m2_pitch = contact.m1m2.height + 2*self.m2_space + self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space) # The rail offsets are indexed by the label self.rails = {} @@ -203,11 +202,12 @@ class hierarchical_predecode(design.design): mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y) self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos]) - z_pos = self.inv_inst[num].get_pin("Z").rc() - self.add_layout_pin_center_segment(text="out[{}]".format(num), - layer="metal1", - start=z_pos, - end=z_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(),0)) + z_pin = self.inv_inst[num].get_pin("Z") + self.add_layout_pin(text="out[{}]".format(num), + layer="metal1", + offset=z_pin.ll(), + height=z_pin.height(), + width=z_pin.width()) def route_input_inverters(self): diff --git a/compiler/sram.py b/compiler/sram.py index 977090be..41156aee 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -48,9 +48,6 @@ class sram(design.design): design.design.__init__(self, name) - # For different layer width vias - self.m2m3_offset_fix = vector(0,0.5*(self.m3_width-self.m2_width)) - # M1/M2 routing pitch is based on contacted pitch of the biggest layer self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space,self.m2_space) self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space) @@ -149,7 +146,7 @@ class sram(design.design): self.control_logic_inputs=self.control_logic.get_inputs() self.control_logic_outputs=self.control_logic.get_outputs() - self.add_pin_list(self.control_logic_inputs + ["clk"],"INPUT") + self.add_pin_list(self.control_logic_inputs,"INPUT") self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") @@ -168,7 +165,7 @@ class sram(design.design): self.route_four_banks() else: debug.error("Invalid number of banks.",-1) - + def add_four_bank_modules(self): """ Adds the modules and the buses to the top level """ @@ -388,7 +385,8 @@ class sram(design.design): offset=self.bank_sel_bus_offset, names=self.bank_sel_bus_names, length=self.vertical_bus_height, - vertical=True)) + vertical=True, + make_pins=True)) # Horizontal data bus @@ -797,7 +795,7 @@ class sram(design.design): # Create the address and control flops (but not the clk) dff_size = self.addr_size + len(self.control_logic.get_inputs())-1 - self.addr_ctrl_dff = self.mod_dff_array(rows=dff_size, columns=1) + self.addr_ctrl_dff = self.mod_dff_array(name="dff_array", rows=dff_size, columns=1) self.add_mod(self.addr_ctrl_dff) # Create the bank module (up to four are instantiated) @@ -853,7 +851,7 @@ class sram(design.design): for i in range(self.word_size): temp.append("DATA[{0}]".format(i)) for i in range(self.bank_addr_size): - temp.append("ADDR[{0}]".format(i)) + temp.append("A[{0}]".format(i)) if(self.num_banks > 1): temp.append("bank_sel[{0}]".format(bank_num)) temp.extend(["s_en", "w_en", "tri_en_bar", "tri_en", @@ -924,10 +922,17 @@ class sram(design.design): def add_control_logic(self, position): """ Add and place control logic """ + inputs = [] + for i in self.control_logic_inputs: + if i != "clk": + inputs.append(i+"_s") + else: + inputs.append(i) + self.control_logic_inst=self.add_inst(name="control", mod=self.control_logic, offset=position) - self.connect_inst(self.control_logic_inputs + self.control_logic_outputs + ["vdd", "gnd"]) + self.connect_inst(inputs + self.control_logic_outputs + ["vdd", "gnd"]) def add_lvs_correspondence_points(self): @@ -986,10 +991,9 @@ class sram(design.design): for (old,new) in zip(ctrl_flops,["CSb","WEb","OEb"]): self.copy_layout_pin(self.addr_ctrl_dff_inst, old, new) - self.copy_layout_pin(self.control_logic_inst, "clk") - self.copy_layout_pin(self.bank_inst, "vdd") - self.copy_layout_pin(self.bank_inst, "gnd") - + self.copy_layout_pin(self.addr_ctrl_dff_inst, "clk") + + # Power ring contains the power pins def add_two_banks(self): # Placement of bank 0 (left) @@ -1030,9 +1034,9 @@ class sram(design.design): in_pos = src_pin.rc() out_pos = vector(dest_pin.cx(), in_pos.y) self.add_wire(("metal3","via2","metal2"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)]) - self.add_via(layers=("metal2","via2","metal3"), - offset=src_pin.lr() - self.m2m3_offset_fix, - rotate=90) + self.add_via_center(layers=("metal2","via2","metal3"), + offset=src_pin.rc(), + rotate=90) def connect_rail_from_left_m2m1(self, src_pin, dest_pin): """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ @@ -1042,34 +1046,41 @@ class sram(design.design): def route_single_bank(self): """ Route a single bank SRAM """ + + # Route the outputs from the control logic module for n in self.control_logic_outputs: src_pin = self.control_logic_inst.get_pin(n) dest_pin = self.bank_inst.get_pin(n) self.connect_rail_from_left_m2m3(src_pin, dest_pin) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=src_pin.rc(), + rotate=90) + - # Expand the ring around the bank + # Expand the ring around the bank to include flops and control logic bbox_lr = vector(self.control_logic_inst.lx(), self.bank_inst.by() + 2*self.supply_rail_pitch) bbox_ur = self.bank_inst.ur() - vector(2*self.supply_rail_pitch, 2*self.supply_rail_pitch) self.add_power_ring([bbox_lr, bbox_ur]) self.route_single_bank_vdd() self.route_single_bank_gnd() - + + # Connect the output of the flops to the bank pins for i in range(self.addr_size): flop_name = "dout[{}]".format(i) bank_name = "A[{}]".format(i) flop_pin = self.addr_ctrl_dff_inst.get_pin(flop_name) bank_pin = self.bank_inst.get_pin(bank_name) flop_pos = flop_pin.center() - bank_pos = bank_pin.lc() - mid_x_pos = 0.5*(flop_pos.x + bank_pos.x) - mid_pos = vector(mid_x_pos - i*self.m2_pitch, flop_pos.y) - self.add_wire(("metal1","via1","metal2"),[flop_pos, mid_pos, bank_pos]) - # There should be M1 in the flop already, but just in case - self.add_via_center(layers=("metal1","via1","metal2"), + bank_pos = vector(bank_pin.cx(),flop_pos.y) + self.add_path("metal3",[flop_pos, bank_pos]) + self.add_via_center(layers=("metal2","via2","metal3"), offset=flop_pos, rotate=90) + self.add_via_center(layers=("metal2","via2","metal3"), + offset=bank_pos, + rotate=90) - + # Connect the output of the flops to the control pins for i in range(3): flop_name = "dout[{}]".format(self.addr_size+i) ctrl_name = ["csb","web","oeb"][i] @@ -1082,7 +1093,20 @@ class sram(design.design): self.add_via_center(layers=("metal2","via2","metal3"), offset=flop_pos, rotate=90) - + + # Connect the clock between the flops and control module + # FIXME: Buffered clock should drive the flops, but then + # it would change the setup time... + flop_pin = self.addr_ctrl_dff_inst.get_pin("clk") + ctrl_pin = self.control_logic_inst.get_pin("clk") + flop_pos = flop_pin.uc() + ctrl_pos = ctrl_pin.bc() + mid_ypos = 0.5*(ctrl_pos.y+flop_pos.y) + mid1_pos = vector(flop_pos.x, mid_ypos) + mid2_pos = vector(ctrl_pos.x, mid_ypos) + self.add_wire(("metal1","via1","metal2"),[flop_pin.uc(), mid1_pos, mid2_pos, ctrl_pin.bc()]) + + def route_single_bank_vdd(self): """ Route vdd for the control and dff array """ @@ -1100,6 +1124,17 @@ class sram(design.design): size = (1,3), rotate=90) + # Route the vdd rails to the TOP + for vdd_pin in self.control_logic_inst.get_pins("vdd"): + if vdd_pin.layer != "metal2": + continue + vdd_pos = vdd_pin.uc() + top_rail_pos = vector(vdd_pos.x, self.top_vdd_y_center) + self.add_path("metal2", [top_rail_pos, vdd_pos]) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=top_rail_pos, + size = (1,3)) + def route_single_bank_gnd(self): """ Route gnd for the control and dff array """ @@ -1118,6 +1153,16 @@ class sram(design.design): size = (1,3), rotate=90) + # Route the vdd rails to the TOP + for gnd_pin in self.control_logic_inst.get_pins("gnd"): + if gnd_pin.layer != "metal2": + continue + gnd_pos = gnd_pin.uc() + top_rail_pos = vector(gnd_pos.x, self.top_gnd_y_center) + self.add_path("metal2", [top_rail_pos, gnd_pos]) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=top_rail_pos, + size = (1,3)) def sp_write(self, sp_name): diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 8207d373..37e5f1b0 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -135,6 +135,7 @@ def write_netgen_script(cell_name, sp_name): # This circuit has symmetries and needs to be flattened to resolve them or the banks won't pass # Is there a more elegant way to add this when needed? f.write("flatten class {{{0}.spice precharge_array}}\n".format(cell_name)) + f.write("flatten class {{{0}.spice dff_array}}\n".format(cell_name)) f.write("property {{nfet {0}.spice}} remove as ad ps pd\n".format(cell_name)) f.write("property {{pfet {0}.spice}} remove as ad ps pd\n".format(cell_name)) f.write("property {{n {0}}} remove as ad ps pd\n".format(sp_name)) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 528c6978..98e0fab3 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -85,8 +85,10 @@ drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/freepdk45/layers.map" drc["minwidth_tx"]=0.09 drc["minlength_channel"] = 0.05 -# WELL.1 Minimum spacing of nwell/pwell at different potential +# WELL.2 Minimum spacing of nwell/pwell at different potential drc["pwell_to_nwell"] = 0.225 +# WELL.3 Minimum spacing of nwell/pwell at the same potential +drc["well_to_well"] = 0.135 # WELL.4 Minimum width of nwell/pwell drc["minwidth_well"] = 0.2 diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index faafb7db..0bba8754 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -70,10 +70,13 @@ drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/scn3me_subm/layers.map" drc["minwidth_tx"] = 1.2 drc["minlength_channel"] = 0.6 +# 1.3 Minimum spacing between wells of same type (if both are drawn) +drc["well_to_well"] = 1.8 # 1.4 Minimum spacing between wells of different type (if both are drawn) drc["pwell_to_nwell"] = 0 # 1.1 Minimum width -drc["minwidth_well"] = 3.6 +drc["minwidth_well"] = 3.6 + # 3.1 Minimum width drc["minwidth_poly"] = 0.6 # 3.2 Minimum spacing over active