from math import log import design from tech import drc, parameter import debug from ms_flop_array import ms_flop_array from wordline_driver import wordline_driver from contact import contact from pinv import pinv from nand_2 import nand_2 from nand_3 import nand_3 from nor_2 import nor_2 from replica_bitline import replica_bitline import math from vector import vector from globals import OPTS class control_logic(design.design): """ Dynamically generated Control logic for the total SRAM circuit. """ def __init__(self, num_rows): """ Constructor """ design.design.__init__(self, "control_logic") debug.info(1, "Creating %s" % self.name) self.num_rows = num_rows self.create_layout() self.DRC_LVS() def create_layout(self): """ Create layout and route between modules """ self.create_modules() self.setup_layout_offsets() self.add_modules() self.add_routing() self.add_pin_labels() def create_modules(self): """ add all the required modules """ c = reload(__import__(OPTS.config.ms_flop)) self.mod_ms_flop = getattr(c, OPTS.config.ms_flop) self.ms_flop = self.mod_ms_flop("ms_flop") self.add_mod(self.ms_flop) self.inv = pinv(name="pinv", nmos_width=drc["minwidth_tx"], beta=parameter["pinv_beta"]) self.add_mod(self.inv) self.nand2 = nand_2(name="nand2", nmos_width=2 * drc["minwidth_tx"]) self.add_mod(self.nand2) self.NAND3 = nand_3(name="NAND3", nmos_width=3 * drc["minwidth_tx"]) self.add_mod(self.NAND3) # Special gates: 4x Inverter self.inv4 = pinv(name="pinv4", nmos_width=4 * drc["minwidth_tx"], beta=parameter["pinv_beta"]) self.add_mod(self.inv4) self.nor2 = nor_2(name="nor2", nmos_width=drc["minwidth_tx"]) self.add_mod(self.nor2) self.msf_control = ms_flop_array(name="msf_control", array_type="data_in", columns=3, word_size=3) self.add_mod(self.msf_control) self.replica_bitline = replica_bitline("replica_bitline", int(math.ceil(self.num_rows / 10.0))) self.add_mod(self.replica_bitline) def add_pin_labels(self): """ Add pins and labels after everything is done """ input_lst =["CSb","WEb","OEb"] output_lst = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar"] clk =["clk"] rails = ["vdd", "gnd"] pin_lst = input_lst + output_lst + clk + rails for pin in pin_lst: self.add_pin(pin) # add label of input, output and clk in metal3 layer input_lst =["CSb","WEb","OEb"] output_lst = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar"] for pin in input_lst + output_lst + ["clk"]: self.add_label(text=pin, layer="metal3", offset=getattr(self, pin+"_position")) # add label of vdd and gnd manually cause non-uniformed names and layers self.add_label(text="vdd", layer="metal1", offset=self.vdd1_position) self.add_label(text="vdd", layer="metal2", offset=self.vdd2_position) self.add_label(text="gnd", layer="metal2", offset=self.gnd_position) def setup_layout_offsets(self): """ Setup layout offsets, determine the size of the busses etc """ # This isn't for instantiating, but we use it to get the dimensions m1m2_via = contact(layer_stack=("metal1", "via1", "metal2")) # Vertical metal rail gap definition self.metal2_extend_contact = (m1m2_via.second_layer_height - m1m2_via.contact_width) / 2 self.gap_between_rails = self.metal2_extend_contact + drc["metal2_to_metal2"] self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"] self.via_shift = (m1m2_via.second_layer_width - m1m2_via.first_layer_width) / 2 # used to shift contact when connecting to NAND3 C pin down self.contact_shift = (m1m2_via.first_layer_width - m1m2_via.contact_width) / 2 # Common parameters for rails self.rail_width = drc["minwidth_metal2"] self.rail_gap = 2 * drc["metal2_to_metal2"] self.rail_offset_gap = self.rail_width + self.rail_gap # First RAIL Parameters self.num_rails_1 = 6 self.overall_rail_1_gap = (self.num_rails_1 + 1) * self.rail_offset_gap self.rail_1_x_offsets = [] # Second RAIL Parameters self.num_rails_2 = 4 self.overall_rail_2_gap = (self.num_rails_2 + 1) * self.rail_offset_gap self.rail_2_x_offsets = [] # GAP between main control and REPLICA BITLINE self.replica_bitline_gap = self.rail_offset_gap * 2 self.output_port_gap = 3 * drc["minwidth_metal3"] self.logic_height = max(self.replica_bitline.width, 4 * self.inv.height) def add_modules(self): """ Place all the modules """ self.add_msf_control() self.set_msf_control_pins() self.add_1st_row(self.output_port_gap) self.add_2nd_row(self.output_port_gap + 2 * self.inv.height) # Height and width self.height = self.logic_height + self.output_port_gap self.width = self.offset_replica_bitline[0] + self.replica_bitline.height def add_routing(self): """ Routing between modules """ self.add_msf_control_routing() self.add_1st_row_routing() self.add_2nd_row_routing() self.add_vdd_routing() self.add_gnd_routing() self.add_input_routing() self.add_output_routing() def add_msf_control(self): """ ADD ARRAY OF MS_FLOP""" self.offset_msf_control = vector(0, self.logic_height + self.output_port_gap) self.add_inst(name="msf_control", mod=self.msf_control, offset=self.offset_msf_control, mirror="R270") # 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 = ["CSb", "WEb", "OEb", "CS_bar", "CS", "WE_bar", "WE", "OE_bar", "OE", "clk", "vdd", "gnd"] self.connect_inst(temp) def set_msf_control_pins(self): # msf_control inputs correct = vector(0, 0.5 * drc["minwidth_metal2"]) def translate_inputs(pt1,pt2): return pt1 + pt2.rotate().scale(1,-1) - correct # msf_control outputs def translate_outputs(pt1,pt2): return pt1 - correct + vector(self.msf_control.height,- pt2[0]) # set CSS WE OE signal groups(in, out, bar) pt1 = self.offset_msf_control pin_set = ["CSb","WEb","OEb"] pt2in = self.msf_control.din_positions[0:len(pin_set)] pt2out = self.msf_control.dout_positions[0:len(pin_set)] pt2bar = self.msf_control.dout_bar_positions[0:len(pin_set)] for i in range(len(pin_set)): value = translate_inputs(pt1,pt2in[i]) setattr(self,"msf_control_"+pin_set[i]+"_position",value) value = translate_outputs(pt1,pt2out[i]) setattr(self,"msf_control_"+pin_set[i][0:2]+"_bar_position",value) value = translate_outputs(pt1,pt2bar[i]) setattr(self,"msf_control_"+pin_set[i][0:2]+"_position",value) # clk , vdd base = self.offset_msf_control - vector(0.5 * drc["minwidth_metal2"], 0) msf_clk = self.msf_control.clk_positions[0].rotate().scale(1,-1) self.msf_control_clk_position = base + msf_clk msf_vdd = self.msf_control.vdd_positions[0].rotate().scale(1,-1) self.msf_control_vdd_position = base + msf_vdd # gnd self.msf_control_gnd_positions = [] for gnd_offset in self.msf_control.gnd_positions: offset = self.offset_msf_control + vector(self.msf_control.height, - gnd_offset[0]) self.msf_control_gnd_positions.append(offset - correct) def add_1st_row(self,y_off): # inv1 with clk as gate input. msf_control_rotate_x = self.offset_msf_control[0] + self.msf_control.height self.offset_inv1 = vector(msf_control_rotate_x - self.inv4.width, y_off) self.add_inst(name="clk_inverter", mod=self.inv4, offset=self.offset_inv1) self.connect_inst(["clk", "clk_bar", "vdd", "gnd"]) # set pin offset as attr self.inv1_A_position = self.offset_inv1 + self.inv4.A_position.scale(0,1) base = self.offset_inv1 + vector(self.inv4.width, 0) for pin in ["Z_position", "vdd_position", "gnd_position"]: setattr(self, "inv1_"+pin, base + getattr(self.inv4, pin).scale(0,1)) # nor2 self.offset_nor2 = vector(self.nor2.width + 2 * drc["minwidth_metal3"], y_off) self.add_inst(name="nor2", mod=self.nor2, offset=self.offset_nor2, mirror="MY") self.connect_inst(["clk", "OE_bar", "tri_en", "vdd", "gnd"]) self.set_nand2_nor2_pin("nor2",[-1,1]) x_off = msf_control_rotate_x + self.overall_rail_1_gap self.nand_array_position = vector(x_off, y_off) # nand2_1 input: OE, clk_bar output: tri_en_bar self.offset_nand2 = self.nand_array_position self.add_inst(name="nand2_tri_en", mod=self.nand2, offset=self.offset_nand2) self.connect_inst(["OE", "clk_bar", "tri_en_bar", "vdd", "gnd"]) # set pin offset as attr self.set_nand2_nor2_pin("nand2",[1,1]) # REPLICA BITLINE base_x = self.nand_array_position[0] + self.NAND3.width + 3 * self.inv.width total_rail_gap = self.rail_offset_gap + self.overall_rail_2_gap x_off = base_x + total_rail_gap + self.replica_bitline_gap self.offset_replica_bitline = vector(x_off, y_off) self.add_inst(name="replica_bitline", mod=self.replica_bitline, offset=self.offset_replica_bitline, mirror="MX", rotate=90) self.connect_inst(["rblk", "pre_s_en", "vdd", "gnd"]) # BUFFER INVERTERS FOR S_EN # inv_4 input: input: pre_s_en_bar, output: s_en self.offset_inv4 = vector(base_x - 2 * self.inv.width, y_off) self.add_inst(name="inv_s_en1", mod=self.inv, offset=self.offset_inv4, mirror="MY") self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"]) self.set_inv2345_pins(inv_name="inv4", inv_scale=[-1, 1]) # inv_5 input: pre_s_en, output: pre_s_en_bar self.offset_inv5 = vector(base_x - self.inv.width, y_off) self.add_inst(name="inv_s_en2", mod=self.inv, offset=self.offset_inv5, mirror="MY") self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"]) self.set_inv2345_pins(inv_name="inv5", inv_scale=[-1, 1]) # set pin offset as attr pin_offset = self.replica_bitline.en_input_offset.rotate() self.replica_en_offset = self.offset_replica_bitline + pin_offset pin_offset = self.replica_bitline.out_offset.rotate() self.replica_out_offset = self.offset_replica_bitline + pin_offset def add_2nd_row(self, y_off): # Nand3_1 input: OE, clk_bar,CS output: rblk_bar self.offset_nand3_1 = vector(self.nand_array_position[0], y_off) self.add_inst(name="NAND3_rblk_bar", mod=self.NAND3, offset=self.offset_nand3_1, mirror="MX") self.connect_inst(["clk_bar", "OE", "CS", "rblk_bar", "vdd", "gnd"]) # set pin offset as attr self.set_Nand3_pins(nand_name = "nand3_1",nand_scale = [0,-1]) # Nand3_2 input: WE, clk_bar,CS output: w_en_bar self.offset_nand3_2 = vector(self.nand_array_position[0], y_off) self.add_inst(name="NAND3_w_en_bar", mod=self.NAND3, offset=self.offset_nand3_2, mirror="RO") self.connect_inst(["clk_bar", "WE", "CS", "w_en_bar", "vdd", "gnd"]) # set pin offset as attr self.set_Nand3_pins(nand_name = "nand3_2",nand_scale = [0,1]) # connect nand2 and nand3 to inv nand3_to_inv_connection_height = self.NAND3.Z_position[1] - self.inv.A_position[1] + drc["minwidth_metal1"] self.add_rect(layer="metal1", offset=self.nand3_1_Z_position, width=drc["minwidth_metal1"], height=nand3_to_inv_connection_height) self.add_rect(layer="metal1", offset=self.nand3_2_Z_position + vector(0,drc["minwidth_metal1"]), width=drc["minwidth_metal1"], height=-nand3_to_inv_connection_height) # inv_2 input: rblk_bar, output: rblk x_off = self.nand_array_position[0] + self.NAND3.width self.offset_inv2 = vector(x_off, y_off) self.add_inst(name="inv_rblk", mod=self.inv, offset=self.offset_inv2, mirror="MX") self.connect_inst(["rblk_bar", "rblk", "vdd", "gnd"]) # set pin offset as attr self.set_inv2345_pins(inv_name="inv2", inv_scale=[1,-1]) # inv_3 input: w_en_bar, output: pre_w_en self.offset_inv3 = self.offset_inv2 self.add_inst(name="inv_w_en", mod=self.inv, offset=self.offset_inv3, mirror="RO") self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"]) # set pin offset as attr self.set_inv2345_pins(inv_name="inv3", inv_scale=[1, 1]) # BUFFER INVERTERS FOR W_EN x_off = self.nand_array_position[0] + self.NAND3.width + self.inv.width self.offset_inv6 = vector(x_off, y_off) self.add_inst(name="inv_w_en1", mod=self.inv, offset=self.offset_inv6, mirror="RO") self.connect_inst(["pre_w_en", "pre_w_en1", "vdd", "gnd"]) x_off = self.nand_array_position[0] + self.NAND3.width + 2 * self.inv.width self.offset_inv7 = [x_off, y_off] self.add_inst(name="inv_w_en2", mod=self.inv, offset=self.offset_inv7, mirror="RO") self.connect_inst(["pre_w_en1", "w_en", "vdd", "gnd"]) # set pin offset as attr self.inv7_Z_position = self.offset_inv7 + vector(self.inv.width, self.inv.Z_position[1]) def set_nand2_nor2_pin(self,mod,scale): offset = getattr (self, "offset_"+mod) for pin in ["A","B"]: pin_xy = getattr(getattr(self, mod), pin+"_position") setattr(self, mod+"_1_"+pin+"_position", offset + pin_xy.scale(0,1)) base = offset + vector(getattr(self, mod).width,0).scale(scale[0],0) for pin in ["Z","vdd","gnd"]: pin_xy = getattr(getattr(self, mod), pin+"_position") setattr(self, mod+"_1_"+pin+"_position", base + pin_xy.scale(0,1)) def set_Nand3_pins(self,nand_name,nand_scale): base = getattr(self, "offset_"+nand_name) extra = vector(0, drc["minwidth_metal1"]* (1 - nand_scale[1]) *0.5) off1 = base - extra off2 = base - extra + vector(self.NAND3.width, 0) self.set_Nand3_pins_sub(nand_name,["A","B","C"],off1,[0,nand_scale[1]]) self.set_Nand3_pins_sub(nand_name,["Z","vdd","gnd"],off2,[0,nand_scale[1]]) def set_Nand3_pins_sub(self,nand_name,pin_lst,base,nand_scale): for pin in pin_lst: pin_xy = getattr(self.NAND3, pin+"_position").scale(0,nand_scale[1]) setattr(self, nand_name+"_"+pin+"_position", base + pin_xy) def set_inv2345_pins(self,inv_name,inv_scale): base_xy = getattr(self, "offset_"+inv_name) correct= vector(0, (1-inv_scale[1]) * 0.5 * drc["minwidth_metal1"]) # pin A pin_xy = vector(0, self.inv4.A_position.y).scale(0,inv_scale[1]) setattr(self, inv_name+"_A_position", base_xy + pin_xy - correct) # Z, vdd, gnd for pin in ["Z_position", "vdd_position", "gnd_position"]: pin_xy = getattr(self.inv, pin).scale(0,inv_scale[1]) rotated_pin_xy = vector(self.inv.width * inv_scale[0], 0) + pin_xy setattr(self, inv_name+"_"+pin, base_xy + rotated_pin_xy - correct) def add_msf_control_routing(self): # FIRST RAIL : MSF_CONTROL OUTPUT RAIL rail1_start = vector(self.msf_control_WE_position[0], self.output_port_gap) for i in range(self.num_rails_1): correct = vector((i+1) * self.rail_offset_gap, 0) offset = rail1_start + correct self.add_rect(layer="metal2", offset=offset, width=drc["minwidth_metal2"], height=self.logic_height) self.rail_1_x_offsets.append(offset[0]) rail2_start_x = (self.nand_array_position[0] + self.NAND3.width + 3 * self.inv.width + self.rail_offset_gap) for i in range(self.num_rails_2): offset = [rail2_start_x + i * self.rail_offset_gap, self.output_port_gap] self.add_rect(layer="metal2", offset=offset, width=drc["minwidth_metal2"], height=self.logic_height) self.rail_2_x_offsets.append(offset[0]) def add_1st_row_routing(self): # First rail routing left left_side = [] for pin in ["OE_bar","OE","CS","WE"]: left_side.append(getattr(self,"msf_control_"+pin+"_position")) line_indices = [1, 2, 3, 4] for i in range(len(left_side)): offset = left_side[i] line_x_offset = self.rail_1_x_offsets[line_indices[i]] self.add_rect(layer="metal1", offset=offset, width=line_x_offset - offset[0] + drc["minwidth_metal2"], height=drc["minwidth_metal1"]) correct1 = vector(self.gap_between_rails, - self.via_shift) correct2 = vector(self.contact_shift + drc["minwidth_metal2"],0) self.add_via(layers=("metal1", "via1", "metal2"), offset=offset + correct1 - correct2, rotate=90) self.add_via(layers=("metal1", "via1", "metal2"), offset=vector(line_x_offset, offset[1]) + correct1, rotate=90) # First rail routing Right right_side = [] right_side.append(self.nand2_1_A_position) right_side.append(self.nand2_1_B_position) for size in ["1","2"]: for pin in ["A","B","C"]: right_side.append(getattr(self,"nand3_"+size+"_"+pin+"_position")) line_indices = [2, 5, 5, 2, 3, 5, 4, 3] for i in range(len(right_side)): offset = right_side[i] line_x_offset = self.rail_1_x_offsets[line_indices[i]] base = vector(line_x_offset, offset[1]) self.add_rect(layer="metal1", offset=base, width=offset[0] - line_x_offset, height=drc["minwidth_metal1"]) self.add_via(layers=("metal1", "via1", "metal2"), offset=base + correct1, rotate=90) # OE_bar [Bus # 1] to nor2 B input layer_stack = ("metal2", "via1", "metal1") start = self.nor2_1_B_position mid1 = [self.nor2_1_B_position[0] + 2 * drc["minwidth_metal2"], start[1]] mid2 = [mid1[0], self.nor2_1_gnd_position[1] - 2 * drc["minwidth_metal1"]] mid3 = [self.rail_1_x_offsets[1] + 0.5 * drc["minwidth_metal2"], mid2[1]] end = [mid3[0], self.output_port_gap] self.add_wire(layer_stack, [start, mid1, mid2, mid3, end]) layer_stack = ("metal1") start = [self.inv1_Z_position[0], self.inv1_Z_position[1] + 0.5 * drc["minwidth_metal1"]] mid1 = [start[0] + drc["minwidth_metal2"], start[1]] mid2 = [mid1[0], self.nand2_1_B_position [1] + 0.5 * drc["minwidth_metal1"]] end = [self.nand2_1_B_position[0], mid2[1]] self.add_path(layer_stack, [start, mid1, mid2, end]) def add_2nd_row_routing(self): # Second rail routing left_side = [] left_side.append(self.inv2_Z_position) left_side.append(self.inv7_Z_position) left_side.append(self.inv5_A_position) line_indices = [1, 0, 2] #line_indices = [1,2] for i in range(len(left_side)): offset = left_side[i] line_x_offset = self.rail_2_x_offsets[line_indices[i]] self.add_rect(layer="metal1", offset=offset, width=line_x_offset - offset[0] + drc["minwidth_metal2"], height=drc["minwidth_metal1"]) self.add_via(layers=("metal1", "via1", "metal2"), offset=[line_x_offset + self.gap_between_rails, offset[1] - self.via_shift], rotate=90) # Replica bitline (rblk to replica bitline input) layer_stack = ("metal2", "via1", "metal1") start = [self.rail_2_x_offsets[1] + 0.5 * drc["minwidth_metal2"], self.output_port_gap] mid1 = [start[0], 0.5 * drc["minwidth_metal1"]] end = [self.replica_en_offset[0], mid1[1]] self.add_wire(layer_stack, [start, mid1, end]) height = self.replica_en_offset[1] - end[1] + 0.5 * drc["minwidth_metal1"] self.add_rect(layer="metal1", offset=end - vector([0.5 * drc["minwidth_metal1"]] * 2), width=drc["minwidth_metal1"], height=height) # Replica bitline (replica bitline output to the buffer [inv4,inv5]) start = [self.rail_2_x_offsets[2], self.replica_out_offset[1]] end = self.replica_out_offset - vector(0.5 * drc["minwidth_metal1"],0) self.add_rect(layer="metal3", offset=start, width=self.replica_out_offset[0] - self.rail_2_x_offsets[2], height=drc["minwidth_metal3"]) self.add_via(layers=("metal2", "via2", "metal3"), offset=start) self.add_via(layers=("metal2", "via2", "metal3"), offset=end) def add_vdd_routing(self): """ VDD routing between modules """ vdd_rail_index = self.num_rails_2 - 1 rail_2_x = self.rail_2_x_offsets[vdd_rail_index] # Connection between nor2 vdd to nand3 vdd self.add_rect(layer="metal1", offset=self.nor2_1_vdd_position, width=rail_2_x + drc["minwidth_metal2"], height=drc["minwidth_metal1"]) # Connection between top AND_Array vdd to the last line on rail2 self.add_rect(layer="metal1", offset=self.nand3_2_vdd_position, width=(rail_2_x + drc["minwidth_metal2"] - self.nand3_2_vdd_position[0]), height=drc["minwidth_metal1"]) # Connection in horizontal metal2 vdd rail base = vector(rail_2_x + self.gap_between_rails, - self.via_shift) self.add_via(layers=("metal1", "via1", "metal2"), offset=base + self.nand2_1_vdd_position.scale(0, 1), rotate=90) self.add_via(layers=("metal1", "via1", "metal2"), offset=base + self.nand3_2_vdd_position.scale(0, 1), rotate=90) # Connection of msf_vdd to inv1 vdd self.add_rect(layer="metal1", offset=[self.msf_control_vdd_position[0], self.inv1_vdd_position[1]], width=drc["minwidth_metal1"], height=self.msf_control_vdd_position[1] - self.inv1_vdd_position[1]) vdd_offset = vector(self.replica_bitline.height,3 * drc["minwidth_metal1"]) self.vdd1_position = vdd_offset + self.offset_replica_bitline self.vdd2_position = vector(rail_2_x, self.output_port_gap) def add_gnd_routing(self): """ GND routing """ self.gnd_position = self.offset_replica_bitline # Connection of msf_control gnds to the metal2 gnd rail for gnd_offset in self.msf_control_gnd_positions: self.add_rect(layer="metal2", offset=gnd_offset, width=(self.rail_1_x_offsets[0] - gnd_offset[0] + drc["minwidth_metal2"]), height=drc["minwidth_metal2"]) # Connect msf_control gnd to nand3 gnd self.add_rect(layer="metal1", offset=self.nor2_1_gnd_position, width=self.offset_replica_bitline[0], height=drc["minwidth_metal1"]) self.add_via(layers=("metal1", "via1", "metal2"), offset=[self.rail_1_x_offsets[0] + self.gap_between_rails, self.nor2_1_gnd_position[1] - self.via_shift], rotate=90) # nand3 gnd to replica bitline gnd self.add_rect(layer="metal1", offset=self.nand3_2_gnd_position, width=(self.offset_replica_bitline[0] - self.nand3_2_gnd_position[0]), height=drc["minwidth_metal1"]) def add_input_routing(self): """ Input pin routing """ # WEb, CEb, OEb assign from msf_control pin self.WEb_position = self.msf_control_WEb_position self.CSb_position = self.msf_control_CSb_position self.OEb_position = self.msf_control_OEb_position # Clk clk_y = self.inv1_vdd_position[1] + 6 * drc["minwidth_metal1"] self.clk_position = vector(0, clk_y) # clk port to inv1 A layer_stack = ("metal2", "via1", "metal1") start = self.inv1_A_position + vector(0, 0.5 * drc["minwidth_metal1"]) mid1 = vector(self.inv1_A_position[0] - 2 * drc["minwidth_metal2"], start.y) mid2 = vector(mid1.x, clk_y) self.clk_position = vector(0, mid2[1]) self.add_wire(layer_stack, [start, mid1, mid2, self.clk_position]) # clk line to msf_control_clk self.add_rect(layer="metal1", offset=[self.msf_control_clk_position[0], self.clk_position[1]], width=drc["minwidth_metal1"], height=(self.msf_control_clk_position[1] - self.clk_position[1])) # clk connection to nor2 A input start = [self.inv1_A_position[0] - 2 * drc["minwidth_metal2"], self.inv1_A_position[1] + 0.5 * drc["minwidth_metal1"]] mid1 = [start[0] - 3 * drc["minwidth_metal2"], start[1]] mid2 = [mid1[0], self.nor2_1_A_position[1]] self.add_path("metal1", [start, mid1, mid2, self.nor2_1_A_position]) correct = vector(0, 0.5 * drc["minwidth_metal1"]) self.add_via(layers=("metal1", "via1", "metal2"), offset=self.clk_position + correct, rotate=270) self.add_via(layers=("metal2", "via2", "metal3"), offset=self.clk_position + correct, rotate=270) def add_output_routing(self): """ Output pin routing """ # clk_bar self.clk_bar_position = vector(self.rail_1_x_offsets[self.num_rails_1 - 1], 0) self.add_rect(layer="metal2", offset=self.clk_bar_position, width=drc["minwidth_metal2"], height=self.output_port_gap) self.add_via(layers=("metal2", "via2", "metal3"), offset=self.clk_bar_position) # tri_en correct = vector (0, 0.5 * drc["minwidth_metal1"]) self.add_via(layers=("metal1", "via1", "metal2"), offset=self.nor2_1_Z_position + correct, rotate=270) self.add_rect(layer="metal2", offset=self.nor2_1_Z_position.scale(1, 0), width=drc["minwidth_metal2"], height=self.nor2_1_Z_position.y + correct.y) self.add_via(layers=("metal2", "via2", "metal3"), offset=self.nor2_1_Z_position.scale(1, 0)) self.tri_en_position = vector(self.nor2_1_Z_position[0], 0) # tri_en_bar correct = vector(drc["minwidth_metal2"], 0) self.tri_en_bar_position = self.nand2_1_Z_position.scale(1, 0) - correct self.add_via(layers=("metal1", "via1", "metal2"), offset=self.nand2_1_Z_position - correct) self.add_rect(layer="metal2", offset=self.tri_en_bar_position, width=drc["minwidth_metal2"], height=self.nand2_1_Z_position[1] + drc["minwidth_metal1"]) self.add_via(layers=("metal2", "via2", "metal3"), offset=self.tri_en_bar_position) # w_en self.w_en_position = vector(self.rail_2_x_offsets[0], 0) self.add_rect(layer="metal2", offset=self.w_en_position, width=drc["minwidth_metal2"], height=self.output_port_gap) self.add_via(layers=("metal2", "via2", "metal3"), offset=self.w_en_position) # s_en self.s_en_position = self.inv4_Z_position.scale(1,0) self.add_via(layers=("metal1", "via1", "metal2"), offset=self.inv4_Z_position) self.add_rect(layer="metal2", offset=self.s_en_position, width=drc["minwidth_metal2"], height=self.inv4_Z_position[1] + drc["minwidth_metal1"]) self.add_via(layers=("metal2", "via2", "metal3"), offset=self.s_en_position)