diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 1d4beb11..a2758c56 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -73,21 +73,21 @@ class contact(hierarchy_design.hierarchy_design): self.second_layer_name = second_layer def setup_layout_constants(self): - self.contact_width = drc["minwidth_{0}". format(self.via_layer_name)] - contact_to_contact = drc["{0}_to_{0}".format(self.via_layer_name)] + self.contact_width = drc("minwidth_{0}". format(self.via_layer_name)) + contact_to_contact = drc("{0}_to_{0}".format(self.via_layer_name)) self.contact_pitch = self.contact_width + contact_to_contact self.contact_array_width = self.contact_width + (self.dimensions[0] - 1) * self.contact_pitch self.contact_array_height = self.contact_width + (self.dimensions[1] - 1) * self.contact_pitch # DRC rules - first_layer_minwidth = drc["minwidth_{0}".format(self.first_layer_name)] - first_layer_minarea = drc["minarea_{0}".format(self.first_layer_name)] - first_layer_enclosure = drc["{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)] - first_layer_extend = drc["{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)] - second_layer_minwidth = drc["minwidth_{0}".format(self.second_layer_name)] - second_layer_minarea = drc["minarea_{0}".format(self.second_layer_name)] - second_layer_enclosure = drc["{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)] - second_layer_extend = drc["{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)] + first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name)) + first_layer_minarea = drc("minarea_{0}".format(self.first_layer_name)) + first_layer_enclosure = drc("{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)) + first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)) + second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name)) + second_layer_minarea = drc("minarea_{0}".format(self.second_layer_name)) + second_layer_enclosure = drc("{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)) + second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)) self.first_layer_horizontal_enclosure = max((first_layer_minwidth - self.contact_array_width) / 2, first_layer_enclosure) @@ -145,16 +145,16 @@ class contact(hierarchy_design.hierarchy_design): height=self.second_layer_height) def create_implant_well_enclosures(self): - implant_position = self.first_layer_position - [drc["implant_enclosure_active"]]*2 - implant_width = self.first_layer_width + 2*drc["implant_enclosure_active"] - implant_height = self.first_layer_height + 2*drc["implant_enclosure_active"] + implant_position = self.first_layer_position - [drc("implant_enclosure_active")]*2 + implant_width = self.first_layer_width + 2*drc("implant_enclosure_active") + implant_height = self.first_layer_height + 2*drc("implant_enclosure_active") self.add_rect(layer="{}implant".format(self.implant_type), offset=implant_position, width=implant_width, height=implant_height) - well_position = self.first_layer_position - [drc["well_enclosure_active"]]*2 - well_width = self.first_layer_width + 2*drc["well_enclosure_active"] - well_height = self.first_layer_height + 2*drc["well_enclosure_active"] + well_position = self.first_layer_position - [drc("well_enclosure_active")]*2 + well_width = self.first_layer_width + 2*drc("well_enclosure_active") + well_height = self.first_layer_height + 2*drc("well_enclosure_active") self.add_rect(layer="{}well".format(self.well_type), offset=well_position, width=well_width, diff --git a/compiler/base/design.py b/compiler/base/design.py index f116d9fa..976b947a 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -29,24 +29,24 @@ class design(hierarchy_design): def setup_drc_constants(self): """ These are some DRC constants used in many places in the compiler.""" from tech import drc - self.well_width = drc["minwidth_well"] - self.poly_width = drc["minwidth_poly"] - self.poly_space = drc["poly_to_poly"] - self.m1_width = drc["minwidth_metal1"] - self.m1_space = drc["metal1_to_metal1"] - self.m2_width = drc["minwidth_metal2"] - self.m2_space = drc["metal2_to_metal2"] - self.m3_width = drc["minwidth_metal3"] - self.m3_space = drc["metal3_to_metal3"] - self.active_width = drc["minwidth_active"] - self.contact_width = drc["minwidth_contact"] + self.well_width = drc("minwidth_well") + self.poly_width = drc("minwidth_poly") + self.poly_space = drc("poly_to_poly") + self.m1_width = drc("minwidth_metal1") + self.m1_space = drc("metal1_to_metal1") + self.m2_width = drc("minwidth_metal2") + self.m2_space = drc("metal2_to_metal2") + self.m3_width = drc("minwidth_metal3") + self.m3_space = drc("metal3_to_metal3") + self.active_width = drc("minwidth_active") + self.contact_width = drc("minwidth_contact") - self.poly_to_active = drc["poly_to_active"] - self.poly_extend_active = drc["poly_extend_active"] - self.contact_to_gate = drc["contact_to_gate"] - self.well_enclose_active = drc["well_enclosure_active"] - self.implant_enclose_active = drc["implant_enclosure_active"] - self.implant_space = drc["implant_to_implant"] + self.poly_to_active = drc("poly_to_active") + self.poly_extend_active = drc("poly_extend_active") + self.contact_to_gate = drc("contact_to_gate") + self.well_enclose_active = drc("well_enclosure_active") + self.implant_enclose_active = drc("implant_enclosure_active") + self.implant_space = drc("implant_to_implant") def setup_multiport_constants(self): """ These are contants and lists that aid multiport design """ diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 95a060f1..7565c6ce 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -63,7 +63,7 @@ class pin_layout: and return the new rectangle. """ if not spacing: - spacing = 0.5*drc["{0}_to_{0}".format(self.layer)] + spacing = 0.5*drc("{0}_to_{0}".format(self.layer)) (ll,ur) = self.rect spacing = vector(spacing, spacing) diff --git a/compiler/base/route.py b/compiler/base/route.py index f8083835..0397e383 100644 --- a/compiler/base/route.py +++ b/compiler/base/route.py @@ -40,9 +40,9 @@ class route(design): (self.horiz_layer_width, self.num_vias, self.vert_layer_width) = self.layer_widths if not self.vert_layer_width: - self.vert_layer_width = drc["minwidth_{0}".format(self.vert_layer_name)] + self.vert_layer_width = drc("minwidth_{0}".format(self.vert_layer_name)) if not self.horiz_layer_width: - self.horiz_layer_width = drc["minwidth_{0}".format(self.horiz_layer_name)] + self.horiz_layer_width = drc("minwidth_{0}".format(self.horiz_layer_name)) # offset this by 1/2 the via size self.c=contact(self.layer_stack, (self.num_vias, self.num_vias)) diff --git a/compiler/base/wire.py b/compiler/base/wire.py index 9220c77a..1565d10e 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -33,14 +33,14 @@ class wire(path): self.via_layer_name = via_layer self.vert_layer_name = vert_layer - self.vert_layer_width = drc["minwidth_{0}".format(vert_layer)] + self.vert_layer_width = drc("minwidth_{0}".format(vert_layer)) self.horiz_layer_name = horiz_layer - self.horiz_layer_width = drc["minwidth_{0}".format(horiz_layer)] + self.horiz_layer_width = drc("minwidth_{0}".format(horiz_layer)) via_connect = contact(self.layer_stack, (1, 1)) - self.node_to_node = [drc["minwidth_" + str(self.horiz_layer_name)] + via_connect.width, - drc["minwidth_" + str(self.horiz_layer_name)] + via_connect.height] + self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width, + drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height] # create a 1x1 contact def create_vias(self): diff --git a/compiler/drc/design_rules.py b/compiler/drc/design_rules.py index ff156e61..465f14ed 100644 --- a/compiler/drc/design_rules.py +++ b/compiler/drc/design_rules.py @@ -1,3 +1,4 @@ +import debug from drc_value import * from drc_lut import * @@ -13,7 +14,11 @@ class design_rules(): self.rules[name] = value def __call__(self, name, *args): - return self.rules[name](args) + rule = self.rules[name] + if callable(rule): + return rule(args) + else: + return rule def __setitem__(self, b, c): """ @@ -26,10 +31,10 @@ class design_rules(): For backward compatibility with existing rules. """ rule = self.rules[b] - if callable(rule): - return rule() - else: + if not callable(rule): return rule + else: + debug.error("Must call complex DRC rule {} with arguments.".format(b),-1) diff --git a/compiler/drc/drc_lut.py b/compiler/drc/drc_lut.py index 514829e3..e6bb8004 100644 --- a/compiler/drc/drc_lut.py +++ b/compiler/drc/drc_lut.py @@ -12,18 +12,16 @@ class drc_lut(): def __init__(self, table): self.table = table - def __call__(self, *args): + def __call__(self, *key): """ Lookup a given tuple in the table. """ - - key_tuple = args - if not key_tuple: + if len(*key)==0: key_size = len(list(self.table.keys())[0]) - key_tuple = tuple(0 for i in range(key_size)) - for key in sorted(self.table.keys(), reverse=True): - if self.match(key_tuple, key): - return self.table[key] + key = tuple(0 for i in range(key_size)) + for table_key in sorted(self.table.keys(), reverse=True): + if self.match(key, table_key): + return self.table[table_key] def match(self, t1, t2): """ diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 54270898..9ef7a2c1 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -200,7 +200,7 @@ class bank(design.design): self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width # A space for wells or jogging m2 - self.m2_gap = max(2*drc["pwell_to_nwell"] + drc["well_enclosure_active"], + self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), 2*self.m2_pitch) @@ -530,7 +530,7 @@ class bank(design.design): # Place the col decoder right aligned with row decoder x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) - y_off = -(self.col_decoder.height + 2*drc["well_to_well"]) + y_off = -(self.col_decoder.height + 2*drc("well_to_well")) col_decoder_inst.place(vector(x_off,y_off)) @@ -567,7 +567,7 @@ class bank(design.design): y_off = min(self.col_decoder_inst[port].by(), self.col_mux_array_inst[port].by()) else: y_off = self.row_decoder_inst[port].by() - y_off -= (self.bank_select.height + drc["well_to_well"]) + y_off -= (self.bank_select.height + drc("well_to_well")) self.bank_select_pos = vector(x_off,y_off) self.bank_select_inst[port].place(self.bank_select_pos) diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 8af2704f..2b78f739 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -67,7 +67,7 @@ class bank_select(design.design): self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell = self.mod_bitcell() - height = self.bitcell.height + drc["poly_to_active"] + height = self.bitcell.height + drc("poly_to_active") # 1x Inverter self.inv_sel = pinv(height=height) @@ -88,8 +88,8 @@ class bank_select(design.design): def calculate_module_offsets(self): - self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc["pwell_to_nwell"] - self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc["pwell_to_nwell"] + self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") + self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") self.xoffset_inv = max(self.xoffset_nand + self.nand2.width, self.xoffset_nor + self.nor2.width) self.xoffset_bank_sel_inv = 0 self.xoffset_inputs = 0 diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 97c62e63..511ef9a8 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -39,7 +39,7 @@ class bitcell_array(design.design): def create_layout(self): # We increase it by a well enclosure so the precharges don't overlap our wells - self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] + self.m1_width + self.height = self.row_size*self.cell.height + drc("well_enclosure_active") + self.m1_width self.width = self.column_size*self.cell.width + self.m1_width xoffset = 0.0 @@ -199,13 +199,13 @@ class bitcell_array(design.design): return total_power def gen_wl_wire(self): - wl_wire = self.generate_rc_net(int(self.column_size), self.width, drc["minwidth_metal1"]) + wl_wire = self.generate_rc_net(int(self.column_size), self.width, drc("minwidth_metal1")) wl_wire.wire_c = 2*spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell return wl_wire def gen_bl_wire(self): bl_pos = 0 - bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), self.height, drc["minwidth_metal1"]) + bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), self.height, drc("minwidth_metal1")) bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell return bl_wire diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py index ab4c827c..5de24948 100644 --- a/compiler/modules/multibank.py +++ b/compiler/modules/multibank.py @@ -170,7 +170,7 @@ class multibank(design.design): self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width # A space for wells or jogging m2 - self.m2_gap = max(2*drc["pwell_to_nwell"] + drc["well_enclosure_active"], + self.m2_gap = max(2*drc("pwell_to_nwell"] + drc["well_enclosure_active"), 2*self.m2_pitch) @@ -382,7 +382,7 @@ class multibank(design.design): """ # Place the col decoder right aligned with row decoder x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) - y_off = -(self.col_decoder.height + 2*drc["well_to_well"]) + y_off = -(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)) @@ -427,7 +427,7 @@ class multibank(design.design): y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by()) else: y_off = self.row_decoder_inst.by() - y_off -= (self.bank_select.height + drc["well_to_well"]) + y_off -= (self.bank_select.height + drc("well_to_well")) self.bank_select_pos = vector(x_off,y_off) self.bank_select_inst = self.add_inst(name="bank_select", mod=self.bank_select, diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 4bf8b9ce..7e0ee718 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -63,7 +63,7 @@ class precharge_array(design.design): layer="metal1", offset=self.pc_cell.get_pin("en").ll(), width=self.width, - height=drc["minwidth_metal1"]) + height=drc("minwidth_metal1")) for inst in self.local_insts: self.copy_layout_pin(inst, "vdd") @@ -74,13 +74,13 @@ class precharge_array(design.design): self.add_layout_pin(text="bl_{0}".format(i), layer="metal2", offset=bl_pin.ll(), - width=drc["minwidth_metal2"], + width=drc("minwidth_metal2"), height=bl_pin.height()) br_pin = inst.get_pin("br") self.add_layout_pin(text="br_{0}".format(i), layer="metal2", offset=br_pin.ll(), - width=drc["minwidth_metal2"], + width=drc("minwidth_metal2"), height=bl_pin.height()) diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index e84efcf1..6d96ef21 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -186,9 +186,9 @@ class replica_bitline(design.design): # Route the connection to the right so that it doesn't interfere with the cells # Wordlines may be close to each other when tiled, so gnd connections are routed in opposite directions if row % 2 == 0: - vertical_extension = vector(0, 1.5*drc["minwidth_metal1"] + 0.5*contact.m1m2.height) + vertical_extension = vector(0, 1.5*drc("minwidth_metal1") + 0.5*contact.m1m2.height) else: - vertical_extension = vector(0, -1.5*drc["minwidth_metal1"] - 1.5*contact.m1m2.height) + vertical_extension = vector(0, -1.5*drc("minwidth_metal1") - 1.5*contact.m1m2.height) pin_right = pin.rc() pin_extension1 = pin_right + vector(self.m3_pitch,0) @@ -202,7 +202,7 @@ class replica_bitline(design.design): wl_last = self.wl_list[self.total_ports-1]+"_{}".format(row) pin_last = self.rbl_inst.get_pin(wl_last) - correct = vector(0.5*drc["minwidth_metal1"], 0) + correct = vector(0.5*drc("minwidth_metal1"), 0) self.add_path("metal1", [pin.rc()-correct, pin_last.rc()-correct]) def route_supplies(self): @@ -261,7 +261,7 @@ class replica_bitline(design.design): # 3. Route the contact of previous route to the bitcell WL # route bend of previous net to bitcell WL wl_offset = self.rbc_inst.get_pin(self.wl_list[0]).lc() - wl_mid1 = wl_offset - vector(1.5*drc["minwidth_metal1"], 0) + wl_mid1 = wl_offset - vector(1.5*drc("minwidth_metal1"), 0) wl_mid2 = vector(wl_mid1.x, contact_offset.y) #xmid_point= 0.5*(wl_offset.x+contact_offset.x) #wl_mid1 = vector(xmid_point,contact_offset.y) @@ -274,7 +274,7 @@ class replica_bitline(design.design): pin = self.rbc_inst.get_pin(wl) pin_last = self.rbc_inst.get_pin(wl_last) - correct = vector(0.5*drc["minwidth_metal1"], 0) + correct = vector(0.5*drc("minwidth_metal1"), 0) self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct]) # DRAIN ROUTE diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 1f44a612..32efaeb5 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -132,7 +132,7 @@ class sense_amp_array(design.design): layer="metal1", offset=sclk_offset, width=self.width, - height=drc["minwidth_metal1"]) + height=drc("minwidth_metal1")) def analytical_delay(self, slew, load=0.0): return self.amp.analytical_delay(slew=slew, load=load) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 56333c20..120a9c1d 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -170,23 +170,23 @@ class single_level_column_mux_array(design.design): self.add_rect(layer="metal1", offset=bl_out_offset, width=width, - height=drc["minwidth_metal2"]) + height=drc("minwidth_metal2")) self.add_rect(layer="metal1", offset=br_out_offset, width=width, - height=drc["minwidth_metal2"]) + height=drc("minwidth_metal2")) # Extend the bitline output rails and gnd downward on the first bit of each n-way mux self.add_layout_pin(text="bl_out_{}".format(int(j/self.words_per_row)), layer="metal2", offset=bl_out_offset.scale(1,0), - width=drc['minwidth_metal2'], + width=drc('minwidth_metal2'), height=self.route_height) self.add_layout_pin(text="br_out_{}".format(int(j/self.words_per_row)), layer="metal2", offset=br_out_offset.scale(1,0), - width=drc['minwidth_metal2'], + width=drc('minwidth_metal2'), height=self.route_height) # This via is on the right of the wire @@ -202,7 +202,7 @@ class single_level_column_mux_array(design.design): self.add_rect(layer="metal2", offset=bl_out_offset, - width=drc['minwidth_metal2'], + width=drc('minwidth_metal2'), height=self.route_height-bl_out_offset.y) # This via is on the right of the wire self.add_via(layers=("metal1", "via1", "metal2"), @@ -210,7 +210,7 @@ class single_level_column_mux_array(design.design): rotate=90) self.add_rect(layer="metal2", offset=br_out_offset, - width=drc['minwidth_metal2'], + width=drc('minwidth_metal2'), height=self.route_height-br_out_offset.y) # This via is on the left of the wire self.add_via(layers=("metal1", "via1", "metal2"), diff --git a/compiler/modules/tri_gate_array.py b/compiler/modules/tri_gate_array.py index d6c5e725..5ca992b3 100644 --- a/compiler/modules/tri_gate_array.py +++ b/compiler/modules/tri_gate_array.py @@ -107,14 +107,14 @@ class tri_gate_array(design.design): layer="metal1", offset=en_pin.ll().scale(0, 1), width=width, - height=drc["minwidth_metal1"]) + height=drc("minwidth_metal1")) enbar_pin = self.tri_inst[0].get_pin("en_bar") self.add_layout_pin(text="en_bar", layer="metal1", offset=enbar_pin.ll().scale(0, 1), width=width, - height=drc["minwidth_metal1"]) + height=drc("minwidth_metal1")) diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index e7f6b79b..61fe8c24 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -130,7 +130,7 @@ class write_driver_array(design.design): layer="metal1", offset=self.driver_insts[0].get_pin("en").ll().scale(0,1), width=self.width, - height=drc['minwidth_metal1']) + height=drc('minwidth_metal1')) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index a262e059..fc839270 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -110,7 +110,7 @@ class pgate(design.design): max_y_offset = self.height + 0.5*self.m1_width self.nwell_position = middle_position nwell_height = max_y_offset - middle_position.y - if drc["has_nwell"]: + if drc("has_nwell"): self.add_rect(layer="nwell", offset=middle_position, width=self.well_width, @@ -122,7 +122,7 @@ class pgate(design.design): pwell_position = vector(0,-0.5*self.m1_width) pwell_height = middle_position.y-pwell_position.y - if drc["has_pwell"]: + if drc("has_pwell"): self.add_rect(layer="pwell", offset=pwell_position, width=self.well_width, @@ -138,7 +138,7 @@ class pgate(design.design): layer_stack = ("active", "contact", "metal1") # To the right a spacing away from the pmos right active edge - contact_xoffset = pmos_pos.x + pmos.active_width + drc["active_to_body_active"] + contact_xoffset = pmos_pos.x + pmos.active_width + drc("active_to_body_active") # Must be at least an well enclosure of active down from the top of the well # OR align the active with the top of PMOS active. max_y_offset = self.height + 0.5*self.m1_width @@ -185,7 +185,7 @@ class pgate(design.design): pwell_position = vector(0,-0.5*self.m1_width) # To the right a spacing away from the nmos right active edge - contact_xoffset = nmos_pos.x + nmos.active_width + drc["active_to_body_active"] + contact_xoffset = nmos_pos.x + nmos.active_width + drc("active_to_body_active") # Must be at least an well enclosure of active up from the bottom of the well contact_yoffset = max(nmos_pos.y, self.well_enclose_active - nmos.active_contact.first_layer_height/2) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index a838ead4..0ffd4f66 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -76,8 +76,8 @@ class pinv(pgate.pgate): # This may make the result differ when the layout is created... if OPTS.netlist_only: self.tx_mults = 1 - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") return # Do a quick sanity check and bail if unlikely feasible height @@ -85,16 +85,16 @@ class pinv(pgate.pgate): # Assume we need 3 metal 1 pitches (2 power rails, one between the tx for the drain) # plus the tx height nmos = ptx(tx_type="nmos") - pmos = ptx(width=drc["minwidth_tx"], tx_type="pmos") + pmos = ptx(width=drc("minwidth_tx"), tx_type="pmos") tx_height = nmos.poly_height + pmos.poly_height # rotated m1 pitch or poly to active spacing min_channel = max(contact.poly.width + self.m1_space, - contact.poly.width + 2*drc["poly_to_active"]) + contact.poly.width + 2*drc("poly_to_active")) # This is the extra space needed to ensure DRC rules to the active contacts extra_contact_space = max(-nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc["poly_extend_active"], self.poly_space) + drc("poly_extend_active"), self.poly_space) total_height = tx_height + min_channel + 2*self.top_bottom_space debug.check(self.height> total_height,"Cell height {0} too small for simple min height {1}.".format(self.height,total_height)) @@ -103,16 +103,16 @@ class pinv(pgate.pgate): # Divide the height in half. Could divide proportional to beta, but this makes # connecting wells of multiple cells easier. # Subtract the poly space under the rail of the tx - nmos_height_available = 0.5 * tx_height_available - 0.5*drc["poly_to_poly"] - pmos_height_available = 0.5 * tx_height_available - 0.5*drc["poly_to_poly"] + nmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly") + pmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly") debug.info(2,"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available, nmos_height_available, pmos_height_available)) # Determine the number of mults for each to fit width into available space - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") nmos_required_mults = max(int(ceil(self.nmos_width/nmos_height_available)),1) pmos_required_mults = max(int(ceil(self.pmos_width/pmos_height_available)),1) # The mults must be the same for easy connection of poly @@ -124,9 +124,9 @@ class pinv(pgate.pgate): # We also need to round the width to the grid or we will end up with LVS property # mismatch errors when fingers are not a grid length and get rounded in the offset geometry. self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults) - debug.check(self.nmos_width>=drc["minwidth_tx"],"Cannot finger NMOS transistors to fit cell height.") + debug.check(self.nmos_width>=drc("minwidth_tx"),"Cannot finger NMOS transistors to fit cell height.") self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults) - debug.check(self.pmos_width>=drc["minwidth_tx"],"Cannot finger PMOS transistors to fit cell height.") + debug.check(self.pmos_width>=drc("minwidth_tx"),"Cannot finger PMOS transistors to fit cell height.") def setup_layout_constants(self): @@ -137,7 +137,7 @@ class pinv(pgate.pgate): # the well width is determined the multi-finger PMOS device width plus # the well contact width and half well enclosure on both sides self.well_width = self.pmos.active_width + self.pmos.active_contact.width \ - + drc["active_to_body_active"] + 2*drc["well_enclosure_active"] + + drc("active_to_body_active") + 2*drc("well_enclosure_active") self.width = self.well_width # Height is an input parameter, so it is not recomputed. diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 14923a84..1a31e3be 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -23,8 +23,8 @@ class pnand2(pgate.pgate): self.nmos_size = 2*size self.pmos_size = parameter["beta"]*size - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnand2 is only supported now.") @@ -91,7 +91,7 @@ class pnand2(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 2*self.pmos.active_width + contact.active.width \ - + 2*drc["active_to_body_active"] + 2*drc["well_enclosure_active"] + + 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -100,7 +100,7 @@ class pnand2(pgate.pgate): extra_contact_space = max(-self.nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc["poly_extend_active"], self.poly_space) + drc("poly_extend_active"), self.poly_space) def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 75887ed3..3247a371 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -25,8 +25,8 @@ class pnand3(pgate.pgate): # If we relax this, we could size this better. self.nmos_size = 2*size self.pmos_size = parameter["beta"]*size - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnand3 is only supported now.") @@ -83,7 +83,7 @@ class pnand3(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 3*self.pmos.active_width + self.pmos.active_contact.width \ - + 2*drc["active_to_body_active"] + 2*drc["well_enclosure_active"] \ + + 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") \ - self.overlap_offset.x self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -96,7 +96,7 @@ class pnand3(pgate.pgate): extra_contact_space = max(-nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc["poly_extend_active"], self.poly_space) + drc("poly_extend_active"), self.poly_space) def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ @@ -191,7 +191,7 @@ class pnand3(pgate.pgate): metal_spacing = max(self.m1_space + self.m1_width, self.m2_space + self.m2_width, self.m1_space + 0.5*contact.poly.width + 0.5*self.m1_width) - active_spacing = max(self.m1_space, 0.5*contact.poly.first_layer_width + drc["poly_to_active"]) + active_spacing = max(self.m1_space, 0.5*contact.poly.first_layer_width + drc("poly_to_active")) inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing self.route_input_gate(self.pmos3_inst, self.nmos3_inst, inputC_yoffset, "C", position="center") diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 87196342..65aaf7f8 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -24,8 +24,8 @@ class pnor2(pgate.pgate): self.nmos_size = size # We will just make this 1.5 times for now. NORs are not ideal anyhow. self.pmos_size = 1.5*parameter["beta"]*size - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnor2 is only supported now.") @@ -92,7 +92,7 @@ class pnor2(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 2*self.pmos.active_width + self.pmos.active_contact.width \ - + 2*drc["active_to_body_active"] + 2*drc["well_enclosure_active"] + + 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -101,7 +101,7 @@ class pnor2(pgate.pgate): extra_contact_space = max(-self.nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc["poly_extend_active"], self.poly_space) + drc("poly_extend_active"), self.poly_space) def add_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 3ddca616..3739c034 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -162,7 +162,7 @@ class precharge(pgate.pgate): """Adds a nwell tap to connect to the vdd rail""" # adds the contact from active to metal1 well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \ - + vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc["well_extend_active"]) + + vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc("well_extend_active")) self.add_contact_center(layers=("active", "contact", "metal1"), offset=well_contact_pos, implant_type="n", @@ -184,7 +184,7 @@ class precharge(pgate.pgate): self.add_layout_pin(text="bl", layer="metal2", offset=offset, - width=drc['minwidth_metal2'], + width=drc("minwidth_metal2"), height=self.height) # adds the BR on metal 2 @@ -192,7 +192,7 @@ class precharge(pgate.pgate): self.add_layout_pin(text="br", layer="metal2", offset=offset, - width=drc['minwidth_metal2'], + width=drc("minwidth_metal2"), height=self.height) def connect_to_bitlines(self): diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index db39c33b..07d04028 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -15,7 +15,7 @@ class ptx(design.design): you to connect the fingered gates and active for parallel devices. """ - def __init__(self, width=drc["minwidth_tx"], mults=1, tx_type="nmos", connect_active=False, connect_poly=False, num_contacts=None): + def __init__(self, width=drc("minwidth_tx"), mults=1, tx_type="nmos", connect_active=False, connect_poly=False, num_contacts=None): # We need to keep unique names because outputting to GDSII # will use the last record with a given name. I.e., you will # over-write a design in GDS if one has and the other doesn't @@ -66,12 +66,12 @@ class ptx(design.design): # self.spice.append("\n.SUBCKT {0} {1}".format(self.name, # " ".join(self.pins))) # Just make a guess since these will actually be decided in the layout later. - area_sd = 2.5*drc["minwidth_poly"]*self.tx_width - perimeter_sd = 2*drc["minwidth_poly"] + 2*self.tx_width + area_sd = 2.5*drc("minwidth_poly")*self.tx_width + perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4}u ps={4}u as={5}p ad={5}p".format(spice[self.tx_type], self.mults, self.tx_width, - drc["minwidth_poly"], + drc("minwidth_poly"), perimeter_sd, area_sd) self.spice.append("\n* ptx " + self.spice_device) @@ -109,7 +109,7 @@ class ptx(design.design): self.contact_pitch = 2*self.contact_to_gate + self.contact_width + self.poly_width # The enclosure of an active contact. Not sure about second term. - active_enclose_contact = max(drc["active_enclosure_contact"], + active_enclose_contact = max(drc("active_enclosure_contact"), (self.active_width - self.contact_width)/2) # This is the distance from the edge of poly to the contacted end of active self.end_to_poly = active_enclose_contact + self.contact_width + self.contact_to_gate @@ -129,7 +129,7 @@ class ptx(design.design): self.active_offset = vector([self.well_enclose_active]*2) # Well enclosure of active, ensure minwidth as well - if drc["has_{}well".format(self.well_type)]: + if drc("has_{}well".format(self.well_type)): self.cell_well_width = max(self.active_width + 2*self.well_enclose_active, self.well_width) self.cell_well_height = max(self.tx_width + 2*self.well_enclose_active, @@ -151,9 +151,9 @@ class ptx(design.design): # Min area results are just flagged for now. - debug.check(self.active_width*self.active_height>=drc["minarea_active"],"Minimum active area violated.") + debug.check(self.active_width*self.active_height>=drc("minarea_active"),"Minimum active area violated.") # We do not want to increase the poly dimensions to fix an area problem as it would cause an LVS issue. - debug.check(self.poly_width*self.poly_height>=drc["minarea_poly"],"Minimum poly area violated.") + debug.check(self.poly_width*self.poly_height>=drc("minarea_poly"),"Minimum poly area violated.") def connect_fingered_poly(self, poly_positions): """ @@ -181,7 +181,7 @@ class ptx(design.design): layer="poly", offset=poly_offset, width=poly_width, - height=drc["minwidth_poly"]) + height=drc("minwidth_poly")) def connect_fingered_active(self, drain_positions, source_positions): @@ -269,7 +269,7 @@ class ptx(design.design): height=self.active_height) # If the implant must enclose the active, shift offset # and increase width/height - enclose_width = drc["implant_enclosure_active"] + enclose_width = drc("implant_enclosure_active") enclose_offset = [enclose_width]*2 self.add_rect(layer="{}implant".format(self.implant_type), offset=self.active_offset - enclose_offset, @@ -280,7 +280,7 @@ class ptx(design.design): """ Add an (optional) well and implant for the type of transistor. """ - if drc["has_{}well".format(self.well_type)]: + if drc("has_{}well".format(self.well_type)): self.add_rect(layer="{}well".format(self.well_type), offset=(0,0), width=self.cell_well_width, diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 140a47d6..ddfca30c 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -52,7 +52,7 @@ class single_level_column_mux(design.design): self.bitcell = self.mod_bitcell() # Adds nmos_lower,nmos_upper to the module - self.ptx_width = self.tx_size * drc["minwidth_tx"] + self.ptx_width = self.tx_size * drc("minwidth_tx") self.nmos = ptx(width=self.ptx_width) self.add_mod(self.nmos) diff --git a/compiler/router/router.py b/compiler/router/router.py index e41f51d8..b2822730 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -1,6 +1,6 @@ import sys import gdsMill -import tech +from tech import drc,GDS,layer from contact import contact import math import debug @@ -31,7 +31,7 @@ class router: self.cell.gds_write(gds_filename) # Load the gds file and read in all the shapes - self.layout = gdsMill.VlsiLayout(units=tech.GDS["unit"]) + self.layout = gdsMill.VlsiLayout(units=GDS["unit"]) self.reader = gdsMill.Gds2reader(self.layout) self.reader.loadFromFile(gds_filename) self.top_name = self.layout.rootStructureName @@ -118,17 +118,18 @@ class router: self.layers = layers (self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers - self.vert_layer_minwidth = tech.drc["minwidth_{0}".format(self.vert_layer_name)] - self.vert_layer_spacing = tech.drc[str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name)] - self.vert_layer_number = tech.layer[self.vert_layer_name] - - self.horiz_layer_minwidth = tech.drc["minwidth_{0}".format(self.horiz_layer_name)] - self.horiz_layer_spacing = tech.drc[str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name)] - self.horiz_layer_number = tech.layer[self.horiz_layer_name] - - # Contacted track spacing. + # This is the minimum routed track spacing via_connect = contact(self.layers, (1, 1)) self.max_via_size = max(via_connect.width,via_connect.height) + + self.vert_layer_minwidth = drc("minwidth_{0}".format(self.vert_layer_name)) + self.vert_layer_spacing = drc(str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name)) + self.vert_layer_number = layer[self.vert_layer_name] + + self.horiz_layer_minwidth = drc("minwidth_{0}".format(self.horiz_layer_name)) + self.horiz_layer_spacing = drc(str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name)) + self.horiz_layer_number = layer[self.horiz_layer_name] + self.horiz_track_width = self.max_via_size + self.horiz_layer_spacing self.vert_track_width = self.max_via_size + self.vert_layer_spacing @@ -263,7 +264,7 @@ class router: """ Scale a shape (two vector list) to user units """ - unit_factor = [tech.GDS["unit"][0]] * 2 + unit_factor = [GDS["unit"][0]] * 2 ll=shape[0].scale(unit_factor) ur=shape[1].scale(unit_factor) return [ll,ur] @@ -470,19 +471,21 @@ class router: return set([best_coord]) - def get_layer_width_space(self, zindex): + def get_layer_width_space(self, zindex, width=0, length=0): """ - Return the width and spacing of a given layer. + Return the width and spacing of a given layer + and wire of a given width and length. """ if zindex==1: - width = self.vert_layer_minwidth - spacing = self.vert_layer_spacing + layer_name = self.vert_layer_name elif zindex==0: - width = self.horiz_layer_minwidth - spacing = self.horiz_layer_spacing + layer_name = self.horiz_layer_name else: debug.error("Invalid zindex for track", -1) + width = drc("minwidth_{0}".format(layer_name), width, length) + spacing = drc(str(layer_name)+"_to_"+str(layer_name), width, length) + return (width,spacing) def convert_pin_coord_to_tracks(self, pin, coord): @@ -1022,31 +1025,6 @@ class router: self.rg.add_target(pin_in_tracks) - def add_supply_rail_target(self, pin_name): - """ - Add the supply rails of given name as a routing target. - """ - debug.info(2,"Add supply rail target {}".format(pin_name)) - for rail in self.supply_rails: - if rail.name != pin_name: - continue - for wave_index in range(len(rail)): - pin_in_tracks = rail[wave_index] - #debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks)) - self.rg.set_target(pin_in_tracks) - self.rg.set_blocked(pin_in_tracks,False) - - def set_supply_rail_blocked(self, value=True): - """ - Add the supply rails of given name as a routing target. - """ - debug.info(3,"Blocking supply rail") - for rail in self.supply_rails: - for wave_index in range(len(rail)): - pin_in_tracks = rail[wave_index] - #debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks)) - self.rg.set_blocked(pin_in_tracks,value) - def set_component_blockages(self, pin_name, value=True): """ Block all of the pin components. @@ -1126,7 +1104,8 @@ class router: def add_wavepath(self, name, path): """ Add the current wave to the given design instance. - This is a single layer path that is multiple tracks wide. + This is a single rectangle that is multiple tracks wide. + It must pay attention to wide metal spacing rules. """ path=self.prepare_path(path) @@ -1149,17 +1128,25 @@ class router: If name is supplied, it is added as a pin and not just a rectangle. """ + + # Find the pin enclosure of the whole track shape (ignoring DRCs) + (abs_ll,unused) = self.convert_track_to_shape(ll) + (unused,abs_ur) = self.convert_track_to_shape(ur) + # Get the layer information - (width, space) = self.get_layer_width_space(zindex) + x_distance = abs(abs_ll.x-abs_ur.x) + y_distance = abs(abs_ll.y-abs_ur.y) + shape_width = min(x_distance, y_distance) + shape_length = max(x_distance, y_distance) + + # Get the DRC rule for the grid dimensions + (width, space) = self.get_layer_width_space(zindex, shape_width, shape_length) layer = self.get_layer(zindex) - # This finds the pin shape enclosed by the track with DRC spacing on the sides - (abs_ll,unused) = self.convert_track_to_pin(ll) - (unused,abs_ur) = self.convert_track_to_pin(ur) - #print("enclose ll={0} ur={1}".format(ll,ur)) - #print("enclose ll={0} ur={1}".format(abs_ll,abs_ur)) - - pin = pin_layout(name, [abs_ll, abs_ur], layer) + # Compute the shape offsets with correct spacing + new_ll = abs_ll + vector(0.5*space, 0.5*space) + new_ur = abs_ur - vector(0.5*space, 0.5*space) + pin = pin_layout(name, [new_ll, new_ur], layer) return pin @@ -1319,7 +1306,7 @@ def snap_to_grid(offset): return vector(xoff, yoff) def snap_val_to_grid(x): - grid = tech.drc["grid"] + grid = drc("grid") xgrid = int(round(round((x / grid), 2), 0)) xoff = xgrid * grid return xoff diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py index e8c65242..880f0c51 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_router.py @@ -244,10 +244,29 @@ class supply_router(router): recent_paths.append(self.paths[-1]) + + def add_supply_rail_target(self, pin_name): + """ + Add the supply rails of given name as a routing target. + """ + debug.info(2,"Add supply rail target {}".format(pin_name)) + for rail in self.supply_rails: + if rail.name != pin_name: + continue + for wave_index in range(len(rail)): + pin_in_tracks = rail[wave_index] + #debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks)) + self.rg.set_target(pin_in_tracks) + self.rg.set_blocked(pin_in_tracks,False) + + def set_supply_rail_blocked(self, value=True): + """ + Add the supply rails of given name as a routing target. + """ + debug.info(3,"Blocking supply rail") + for rail in self.supply_rails: + for wave_index in range(len(rail)): + pin_in_tracks = rail[wave_index] + #debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks)) + self.rg.set_blocked(pin_in_tracks,value) - - - - - -