diff --git a/compiler/base/errors.py b/compiler/base/errors.py new file mode 100644 index 00000000..e2b9e5ec --- /dev/null +++ b/compiler/base/errors.py @@ -0,0 +1,15 @@ + + +class drc_error(Exception): + """Exception raised for DRC errors. + + Attributes: + expression -- input expression in which the error occurred + message -- explanation of the error + """ + + # def __init__(self, expression, message): + # self.expression = expression + # self.message = message + def __init__(self, message): + self.message = message diff --git a/compiler/globals.py b/compiler/globals.py index 3277ab1d..4e6253ad 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -589,4 +589,4 @@ def report_status(): if OPTS.trim_netlist: debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).") if OPTS.nominal_corner_only: - debug.print_raw("Only characterizing nominal corner.") + debug.print_raw("Only characterizing nominal corner.") diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index c3307ece..3be10d3e 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -45,11 +45,9 @@ class bank_select(design.design): self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width self.width = max([x.rx() for x in self.inv_inst]) - self.add_boundary() self.DRC_LVS() - def add_pins(self): # Number of control lines in the bus @@ -65,19 +63,18 @@ class bank_select(design.design): if (self.port == "rw") or (self.port == "r"): self.input_control_signals.append("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] + self.control_signals = ["gated_" + str for str in self.input_control_signals] self.add_pin_list(self.input_control_signals, "INPUT") self.add_pin("bank_sel") self.add_pin_list(self.control_signals, "OUTPUT") - self.add_pin("vdd","POWER") - self.add_pin("gnd","GROUND") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def add_modules(self): """ Create modules for later instantiation """ - self.bitcell = factory.create(module_type="bitcell") - - height = self.bitcell.height + drc("poly_to_active") + self.dff = factory.create(module_type="dff") + height = self.dff.height + drc("poly_to_active") # 1x Inverter self.inv_sel = factory.create(module_type="pinv", height=height) @@ -98,17 +95,15 @@ 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_bank_sel_inv = 0 + self.xoffset_nand = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell") + self.xoffset_nor = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell") + self.xoffset_bank_sel_inv = 0 self.xoffset_inputs = 0 - self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height - def create_instances(self): - self.bank_sel_inv=self.add_inst(name="bank_sel_inv", + self.bank_sel_inv=self.add_inst(name="bank_sel_inv", mod=self.inv_sel) self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"]) @@ -125,36 +120,36 @@ class bank_select(design.design): # (writes occur on clk low) if input_name in ("clk_buf"): - self.logic_inst.append(self.add_inst(name=name_nor, - mod=self.nor2)) + self.logic_inst.append(self.add_inst(name=name_nor, + mod=self.nor2)) self.connect_inst([input_name, "bank_sel_bar", - gated_name+"_temp_bar", + gated_name + "_temp_bar", "vdd", "gnd"]) # They all get inverters on the output - self.inv_inst.append(self.add_inst(name=name_inv, + self.inv_inst.append(self.add_inst(name=name_inv, mod=self.inv4x_nor)) - self.connect_inst([gated_name+"_temp_bar", + self.connect_inst([gated_name + "_temp_bar", gated_name, "vdd", "gnd"]) # the rest are AND (nand2+inv) gates else: - self.logic_inst.append(self.add_inst(name=name_nand, + self.logic_inst.append(self.add_inst(name=name_nand, mod=self.nand2)) self.connect_inst([input_name, "bank_sel", - gated_name+"_temp_bar", + gated_name + "_temp_bar", "vdd", "gnd"]) # They all get inverters on the output - self.inv_inst.append(self.add_inst(name=name_inv, + self.inv_inst.append(self.add_inst(name=name_inv, mod=self.inv4x)) - self.connect_inst([gated_name+"_temp_bar", + self.connect_inst([gated_name + "_temp_bar", gated_name, "vdd", "gnd"]) @@ -177,9 +172,9 @@ class bank_select(design.design): if i == 0: y_offset = 0 else: - y_offset = self.inv4x_nor.height + self.inv4x.height * (i-1) + y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1) - if i%2: + if i % 2: y_offset += self.inv4x.height mirror = "MX" else: @@ -200,7 +195,6 @@ class bank_select(design.design): # They all get inverters on the output inv_inst.place(offset=[logic_inst.rx(), y_offset], mirror=mirror) - def route_instances(self): @@ -222,57 +216,56 @@ class bank_select(design.design): end=bank_sel_pin_end) self.add_via_center(layers=self.m2_stack, offset=bank_sel_pin_end, - directions=("H","H")) + directions=("H", "H")) # bank_sel_bar is vertical wire bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z") xoffset_bank_sel_bar = bank_sel_bar_pin.rx() self.add_label_pin(text="bank_sel_bar", - layer="m2", - offset=vector(xoffset_bank_sel_bar, 0), + layer="m2", + offset=vector(xoffset_bank_sel_bar, 0), height=self.inv4x.height) self.add_via_center(layers=self.m1_stack, offset=bank_sel_bar_pin.rc()) - for i in range(self.num_control_lines): logic_inst = self.logic_inst[i] inv_inst = self.inv_inst[i] input_name = self.input_control_signals[i] - gated_name = self.control_signals[i] + gated_name = self.control_signals[i] if input_name in ("clk_buf"): xoffset_bank_signal = xoffset_bank_sel_bar else: xoffset_bank_signal = xoffset_bank_sel # Connect the logic output to inverter input - pre = logic_inst.get_pin("Z").lc() - out_position = logic_inst.get_pin("Z").rc() + vector(0.5*self.m1_width,0) - in_position = inv_inst.get_pin("A").lc() + vector(0.5*self.m1_width,0) - post = inv_inst.get_pin("A").rc() - self.add_path("m1", [pre, out_position, in_position, post]) + out_pin = logic_inst.get_pin("Z") + out_pos = out_pin.rc() + in_pin = inv_inst.get_pin("A") + in_pos = in_pin.lc() + mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y) + mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y) + self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos]) - - # Connect the logic B input to bank_sel/bank_sel_bar - logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1_via.height,0) + # Connect the logic B input to bank_sel / bank_sel_bar + logic_pos = logic_inst.get_pin("B").lc() - vector(0.5 * contact.m1_via.height, 0) input_pos = vector(xoffset_bank_signal, logic_pos.y) - self.add_path("m2",[logic_pos, input_pos]) + self.add_path("m2", [logic_pos, input_pos]) self.add_via_center(layers=self.m1_stack, offset=logic_pos, - directions=("H","H")) + directions=("H", "H")) - # Connect the logic A input to the input pin logic_pos = logic_inst.get_pin("A").lc() - input_pos = vector(0,logic_pos.y) + input_pos = vector(0, logic_pos.y) self.add_via_center(layers=self.m1_stack, offset=logic_pos, - directions=("H","H")) + directions=("H", "H")) self.add_via_center(layers=self.m2_stack, offset=logic_pos, - directions=("H","H")) + directions=("H", "H")) self.add_layout_pin_segment_center(text=input_name, layer="m3", start=input_pos, @@ -286,7 +279,6 @@ class bank_select(design.design): width=inv_inst.rx() - out_pin.lx(), height=out_pin.height()) - # Find the x offsets for where the vias/pins should be placed a_xoffset = self.logic_inst[0].lx() b_xoffset = self.inv_inst[0].lx() @@ -294,7 +286,7 @@ class bank_select(design.design): # Route both supplies for n in ["vdd", "gnd"]: supply_pin = self.inv_inst[num].get_pin(n) - supply_offset = supply_pin.ll().scale(0,1) + supply_offset = supply_pin.ll().scale(0, 1) self.add_rect(layer="m1", offset=supply_offset, width=self.width) @@ -304,10 +296,10 @@ class bank_select(design.design): pin_pos = vector(xoffset, supply_pin.cy()) self.add_via_center(layers=self.m1_stack, offset=pin_pos, - directions=("H","H")) + directions=("H", "H")) self.add_via_center(layers=self.m2_stack, offset=pin_pos, - directions=("H","H")) + directions=("H", "H")) self.add_layout_pin_rect_center(text=n, layer="m3", offset=pin_pos) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 0a54a401..a805e635 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -11,13 +11,15 @@ import math from sram_factory import factory from vector import vector from globals import OPTS +from errors import drc_error +from tech import cell_properties class hierarchical_decoder(design.design): """ Dynamically generated hierarchical decoder. """ - def __init__(self, name, rows): + def __init__(self, name, num_outputs): design.design.__init__(self, name) self.AND_FORMAT = "DEC_AND_{0}" @@ -25,9 +27,17 @@ class hierarchical_decoder(design.design): self.pre2x4_inst = [] self.pre3x8_inst = [] - (self.cell_height, self.cell_multiple) = self.find_decoder_height() - self.rows = rows - self.num_inputs = math.ceil(math.log(self.rows, 2)) + b = factory.create(module_type="bitcell") + try: + self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple + except AttributeError: + self.cell_multiple = 1 + # For debugging + # self.cell_multiple = 2 + self.cell_height = self.cell_multiple * b.height + + self.num_outputs = num_outputs + self.num_inputs = math.ceil(math.log(self.num_outputs, 2)) (self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) self.create_netlist() @@ -35,20 +45,37 @@ class hierarchical_decoder(design.design): self.create_layout() def find_decoder_height(self): + """ + Dead code. This would dynamically determine the bitcell multiple, + but I just decided to hard code it in the tech file if it is not 1 + because a DRC tool would be required even to run in front-end mode. + """ b = factory.create(module_type="bitcell") + # Old behavior - return (b.height, 1) + if OPTS.netlist_only: + return (b.height, 1) # Search for the smallest multiple that works cell_multiple = 1 - while cell_multiple < 3: + while cell_multiple < 5: cell_height = cell_multiple * b.height - and3 = factory.create(module_type="pand3", - height=cell_height) - (drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True) - if drc_errors + lvs_errors == 0: - return (cell_height, cell_multiple) + # debug.info(2,"Trying mult = {0} height={1}".format(cell_multiple, cell_height)) + try: + and3 = factory.create(module_type="pand3", + height=cell_height) + except drc_error: + # debug.info(1, "Incrementing decoder height by 1 bitcell height {}".format(b.height)) + pass + else: + (drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True) + total_errors = drc_errors + lvs_errors + if total_errors == 0: + debug.info(1, "Decoder height is multiple of {} bitcells.".format(cell_multiple)) + return (cell_height, cell_multiple) + cell_multiple += 1 + else: debug.error("Couldn't find a valid decoder height multiple.", -1) @@ -63,8 +90,8 @@ class hierarchical_decoder(design.design): self.setup_layout_constants() self.place_pre_decoder() self.place_row_decoder() - self.route_input_rails() - self.route_predecode_rails() + self.route_inputs() + self.route_decoder_bus() self.route_vdd_gnd() self.offset_all_coordinates() self.add_boundary() @@ -118,7 +145,7 @@ class hierarchical_decoder(design.design): def setup_netlist_constants(self): self.predec_groups = [] # This array is a 2D array. - # Distributing vertical rails to different groups. One group belongs to one pre-decoder. + # Distributing vertical bus to different groups. One group belongs to one pre-decoder. # For example, for two 2:4 pre-decoder and one 3:8 pre-decoder, we will # have total 16 output lines out of these 3 pre-decoders and they will # be distributed as [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ] @@ -157,39 +184,46 @@ class hierarchical_decoder(design.design): self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 + # We may have more than one bitcell per decoder row + self.num_rows = math.ceil(self.num_outputs / self.cell_multiple) + # We will place this many final decoders per row + self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows) + # Calculates height and width of row-decoder if (self.num_inputs == 4 or self.num_inputs == 5): nand_width = self.and2.width else: nand_width = self.and3.width - self.internal_routing_width = self.m2_pitch * self.total_number_of_predecoder_outputs - self.row_decoder_height = self.inv.height * self.rows + self.internal_routing_width = self.m2_pitch * (self.total_number_of_predecoder_outputs + 1) + self.row_decoder_height = self.inv.height * self.num_rows - self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch + self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch # Calculates height and width of hierarchical decoder - self.height = self.row_decoder_height + # Add extra pitch for good measure + self.height = max(self.predecoder_height, self.row_decoder_height) + self.m3_pitch self.width = self.input_routing_width + self.predecoder_width \ - + self.internal_routing_width + nand_width + self.inv.width + + self.internal_routing_width \ + + self.decoders_per_row * nand_width + self.inv.width - def route_input_rails(self): - """ Create input rails for the predecoders """ + def route_inputs(self): + """ Create input bus for the predecoders """ # inputs should be as high as the decoders input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height # Find the left-most predecoder min_x = 0 if self.no_of_pre2x4 > 0: - min_x = min(min_x, -self.pre2_4.width) + min_x = min(min_x, self.pre2x4_inst[0].lx()) if self.no_of_pre3x8 > 0: - min_x = min(min_x, -self.pre3_8.width) + min_x = min(min_x, self.pre3x8_inst[0].lx()) input_offset=vector(min_x - self.input_routing_width, 0) input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)] - self.input_rails = self.create_vertical_pin_bus(layer="m2", - pitch=self.m2_pitch, - offset=input_offset, - names=input_bus_names, - length=input_height) + self.input_bus = self.create_vertical_pin_bus(layer="m2", + pitch=self.m2_pitch, + offset=input_offset, + names=input_bus_names, + length=input_height) self.route_input_to_predecodes() @@ -199,7 +233,7 @@ class hierarchical_decoder(design.design): for i in range(2): index = pre_num * 2 + i - input_pos = self.input_rails["addr_{}".format(index)] + input_pos = self.input_bus["addr_{}".format(index)] in_name = "in_{}".format(i) decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name) @@ -209,13 +243,13 @@ class hierarchical_decoder(design.design): decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) - self.route_input_rail(decoder_offset, input_offset) + self.route_input_bus(decoder_offset, input_offset) for pre_num in range(self.no_of_pre3x8): for i in range(3): index = pre_num * 3 + i + self.no_of_pre2x4 * 2 - input_pos = self.input_rails["addr_{}".format(index)] + input_pos = self.input_bus["addr_{}".format(index)] in_name = "in_{}".format(i) decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name) @@ -225,10 +259,13 @@ class hierarchical_decoder(design.design): decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) - self.route_input_rail(decoder_offset, input_offset) + self.route_input_bus(decoder_offset, input_offset) - def route_input_rail(self, input_offset, output_offset): - """ Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ + def route_input_bus(self, input_offset, output_offset): + """ + Route a vertical M2 coordinate to another + vertical M2 coordinate to the predecode inputs + """ self.add_via_center(layers=self.m2_stack, offset=input_offset) @@ -242,7 +279,7 @@ class hierarchical_decoder(design.design): for i in range(self.num_inputs): self.add_pin("addr_{0}".format(i), "INPUT") - for j in range(self.rows): + for j in range(self.num_outputs): self.add_pin("decode_{0}".format(j), "OUTPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") @@ -311,18 +348,17 @@ class hierarchical_decoder(design.design): else: base= vector(-self.pre2_4.width, num * self.pre2_4.height) - self.pre2x4_inst[num].place(base) + self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0)) def place_pre3x8(self, num): """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ if (self.num_inputs == 3): 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.pre3_8.width, height) - self.pre3x8_inst[num].place(offset) + self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0)) def create_row_decoder(self): """ Create the row-decoder by placing AND2/AND3 and Inverters @@ -339,14 +375,14 @@ class hierarchical_decoder(design.design): if (self.num_inputs == 4 or self.num_inputs == 5): for i in range(len(self.predec_groups[0])): for j in range(len(self.predec_groups[1])): - row = len(self.predec_groups[0]) * j + i - if (row < self.rows): - name = self.AND_FORMAT.format(row) + output = len(self.predec_groups[0]) * j + i + if (output < self.num_outputs): + name = self.AND_FORMAT.format(output) self.and_inst.append(self.add_inst(name=name, mod=self.and2)) pins =["out_{0}".format(i), "out_{0}".format(j + len(self.predec_groups[0])), - "decode_{0}".format(row), + "decode_{0}".format(output), "vdd", "gnd"] self.connect_inst(pins) @@ -355,18 +391,18 @@ class hierarchical_decoder(design.design): for i in range(len(self.predec_groups[0])): for j in range(len(self.predec_groups[1])): for k in range(len(self.predec_groups[2])): - row = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \ - + len(self.predec_groups[0]) * j + i + output = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \ + + len(self.predec_groups[0]) * j + i - if (row < self.rows): - name = self.AND_FORMAT.format(row) + if (output < self.num_outputs): + name = self.AND_FORMAT.format(output) self.and_inst.append(self.add_inst(name=name, mod=self.and3)) pins = ["out_{0}".format(i), "out_{0}".format(j + len(self.predec_groups[0])), "out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])), - "decode_{0}".format(row), + "decode_{0}".format(output), "vdd", "gnd"] self.connect_inst(pins) @@ -380,7 +416,10 @@ class hierarchical_decoder(design.design): self.route_decoder() def place_decoder_and_array(self): - """ Add a column of AND gates for final decode """ + """ + Add a column of AND gates for final decode. + This may have more than one decoder per row to match the bitcell height. + """ # Row Decoder AND GATE array for address inputs <5. if (self.num_inputs == 4 or self.num_inputs == 5): @@ -392,9 +431,13 @@ class hierarchical_decoder(design.design): self.place_and_array(and_mod=self.and3) def place_and_array(self, and_mod): - """ Add a column of AND gates for the decoder above the predecoders.""" - - for row in range(self.rows): + """ + Add a column of AND gates for the decoder above the predecoders. + """ + + for inst_index in range(self.num_outputs): + row = math.floor(inst_index / self.decoders_per_row) + dec = inst_index % self.decoders_per_row if ((row % 2) == 0): y_off = and_mod.height * row mirror = "R0" @@ -402,46 +445,52 @@ class hierarchical_decoder(design.design): y_off = and_mod.height * (row + 1) mirror = "MX" - self.and_inst[row].place(offset=[self.internal_routing_width, y_off], - mirror=mirror) + x_off = self.internal_routing_width + dec * and_mod.width + self.and_inst[inst_index].place(offset=vector(x_off, y_off), + mirror=mirror) def route_decoder(self): """ Add the pins. """ - for row in range(self.rows): - z_pin = self.and_inst[row].get_pin("Z") - self.add_layout_pin(text="decode_{0}".format(row), + for output in range(self.num_outputs): + z_pin = self.and_inst[output].get_pin("Z") + self.add_layout_pin(text="decode_{0}".format(output), layer="m1", offset=z_pin.ll(), width=z_pin.width(), height=z_pin.height()) - def route_predecode_rails(self): - """ Creates vertical metal 2 rails to connect predecoder and decoder stages.""" + def route_decoder_bus(self): + """ + Creates vertical metal 2 bus to connect predecoder and decoder stages. + """ # This is not needed for inputs <4 since they have no pre/decode stages. if (self.num_inputs >= 4): - input_offset = vector(0.5 * self.m2_width, 0) + # This leaves an offset for the predecoder output jogs input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)] - self.predecode_rails = self.create_vertical_pin_bus(layer="m2", - pitch=self.m2_pitch, - offset=input_offset, - names=input_bus_names, - length=self.height) + self.predecode_bus = self.create_vertical_pin_bus(layer="m2", + pitch=self.m2_pitch, + offset=vector(0, 0), + names=input_bus_names, + length=self.height) - self.route_rails_to_predecodes() - self.route_rails_to_decoder() - - def route_rails_to_predecodes(self): - """ Iterates through all of the predecodes and connects to the rails including the offsets """ + self.route_predecodes_to_bus() + self.route_bus_to_decoder() + def route_predecodes_to_bus(self): + """ + Iterates through all of the predecodes + and connects to the rails including the offsets + """ # FIXME: convert to connect_bus for pre_num in range(self.no_of_pre2x4): for i in range(4): predecode_name = "predecode_{}".format(pre_num * 4 + i) out_name = "out_{}".format(i) pin = self.pre2x4_inst[pre_num].get_pin(out_name) - self.route_predecode_rail_m3(predecode_name, pin) + x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch + self.route_predecode_bus_inputs(predecode_name, pin, x_offset) # FIXME: convert to connect_bus for pre_num in range(self.no_of_pre3x8): @@ -449,52 +498,82 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(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.route_predecode_rail_m3(predecode_name, pin) + x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch + self.route_predecode_bus_inputs(predecode_name, pin, x_offset) - def route_rails_to_decoder(self): - """ Use the self.predec_groups to determine the connections to the decoder AND gates. - Inputs of AND2/AND3 gates come from different groups. - For example for these groups [ [0,1,2,3] ,[4,5,6,7], - [8,9,10,11,12,13,14,15] ] the first AND3 inputs are connected to - [0,4,8] and second AND3 is connected to [0,4,9] ........... and the - 128th AND3 is connected to [3,7,15] + def route_bus_to_decoder(self): """ - row_index = 0 + Use the self.predec_groups to determine the connections to the decoder AND gates. + Inputs of AND2/AND3 gates come from different groups. + For example for these groups + [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ] + the first AND3 inputs are connected to [0,4,8], + second AND3 is connected to [0,4,9], + ... + and the 128th AND3 is connected to [3,7,15] + """ + output_index = 0 + if (self.num_inputs == 4 or self.num_inputs == 5): for index_B in self.predec_groups[1]: for index_A in self.predec_groups[0]: # FIXME: convert to connect_bus? - if (row_index < self.rows): + if (output_index < self.num_outputs): + row_index = math.floor(output_index / self.decoders_per_row) + row_remainder = (output_index % self.decoders_per_row) + row_offset = row_index * self.and_inst[0].height + (2 * row_remainder + 1) * self.m3_pitch predecode_name = "predecode_{}".format(index_A) - self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A")) + self.route_predecode_bus_outputs(predecode_name, + self.and_inst[output_index].get_pin("A"), + row_offset) predecode_name = "predecode_{}".format(index_B) - self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B")) - row_index = row_index + 1 + self.route_predecode_bus_outputs(predecode_name, + self.and_inst[output_index].get_pin("B"), + row_offset + self.m3_pitch) + output_index = output_index + 1 elif (self.num_inputs > 5): for index_C in self.predec_groups[2]: for index_B in self.predec_groups[1]: for index_A in self.predec_groups[0]: # FIXME: convert to connect_bus? - if (row_index < self.rows): + if (output_index < self.num_outputs): + row_index = math.floor(output_index / self.decoders_per_row) + row_remainder = (output_index % self.decoders_per_row) + row_offset = row_index * self.and_inst[0].height + (3 * row_remainder + 1) * self.m3_pitch predecode_name = "predecode_{}".format(index_A) - self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A")) + self.route_predecode_bus_outputs(predecode_name, + self.and_inst[output_index].get_pin("A"), + row_offset) predecode_name = "predecode_{}".format(index_B) - self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B")) + self.route_predecode_bus_outputs(predecode_name, + self.and_inst[output_index].get_pin("B"), + row_offset + self.m3_pitch) predecode_name = "predecode_{}".format(index_C) - self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("C")) - row_index = row_index + 1 + self.route_predecode_bus_outputs(predecode_name, + self.and_inst[output_index].get_pin("C"), + row_offset + 2 * self.m3_pitch) + output_index = output_index + 1 def route_vdd_gnd(self): - """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ + """ + Add a pin for each row of vdd/gnd which are + must-connects next level up. + """ - # The vias will be placed in the center and right of the cells, respectively. - xoffset = self.and_inst[0].rx() - for num in range(0, self.rows): + # The vias will be placed at the right of the cells. + xoffset = max(x.rx() for x in self.and_inst) + for num in range(0, self.num_outputs): + # Only add the power pin for the 1st in each row + if num % self.decoders_per_row: + continue + for pin_name in ["vdd", "gnd"]: # The nand and inv are the same height rows... supply_pin = self.and_inst[num].get_pin(pin_name) pin_pos = vector(xoffset, supply_pin.cy()) + self.add_path("m1", + [supply_pin.lc(), vector(xoffset, supply_pin.cy())]) self.add_power_pin(name=pin_name, loc=pin_pos) @@ -503,23 +582,42 @@ class hierarchical_decoder(design.design): self.copy_layout_pin(pre, "vdd") self.copy_layout_pin(pre, "gnd") - def route_predecode_rail(self, rail_name, pin): - """ Connect the routing rail to the given metal1 pin """ - rail_pos = vector(self.predecode_rails[rail_name].x, pin.lc().y) - self.add_path("m1", [rail_pos, pin.lc()]) - self.add_via_center(layers=self.m1_stack, - offset=rail_pos) + def route_predecode_bus_outputs(self, rail_name, pin, y_offset): + """ + Connect the routing rail to the given metal1 pin + using a routing track at the given y_offset + + """ + pin_pos = pin.center() + # If we have a single decoder per row, we can route on M1 + if self.decoders_per_row == 1: + rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) + self.add_path("m1", [rail_pos, pin_pos]) + self.add_via_center(layers=self.m1_stack, + offset=rail_pos) + # If not, we must route over the decoder cells on M3 + else: + rail_pos = vector(self.predecode_bus[rail_name].x, y_offset) + mid_pos = vector(pin_pos.x, rail_pos.y) + self.add_wire(self.m2_stack[::-1], [rail_pos, mid_pos, pin_pos]) + self.add_via_center(layers=self.m2_stack, + offset=rail_pos) + self.add_via_center(layers=self.m1_stack, + offset=pin_pos) - def route_predecode_rail_m3(self, rail_name, pin): - """ Connect the routing rail to the given metal1 pin """ + def route_predecode_bus_inputs(self, rail_name, pin, x_offset): + """ + Connect the routing rail to the given metal1 pin using a jog + to the right of the cell at the given x_offset. + """ # This routes the pin up to the rail, basically, to avoid conflicts. # It would be fixed with a channel router. - mid_point = vector(pin.cx(), pin.cy() + self.inv.height / 2) - rail_pos = vector(self.predecode_rails[rail_name].x, mid_point.y) + pin_pos = pin.center() + mid_point1 = vector(x_offset, pin_pos.y) + mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2) + rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y) + self.add_wire(self.m1_stack, [pin_pos, mid_point1, mid_point2, rail_pos]) self.add_via_center(layers=self.m1_stack, - offset=pin.center()) - self.add_wire(("m3", "via2", "m2"), [rail_pos, mid_point, pin.uc()]) - self.add_via_center(layers=self.m2_stack, offset=rail_pos) def input_load(self): diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 44b0d5a5..6938219a 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -90,17 +90,14 @@ class port_address(design.design): # The pre/post is to access the pin from "outside" the cell to avoid DRCs decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc() driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc() - mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) - mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) + mid1 = decoder_out_pos.scale(0.5, 1) + driver_in_pos.scale(0.5, 0) + mid2 = decoder_out_pos.scale(0.5, 0) + driver_in_pos.scale(0.5, 1) self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos]) - - - def add_modules(self): self.row_decoder = factory.create(module_type="decoder", - rows=self.num_rows) + num_outputs=self.num_rows) self.add_mod(self.row_decoder) self.wordline_driver = factory.create(module_type="wordline_driver", @@ -108,11 +105,10 @@ class port_address(design.design): cols=self.num_cols) self.add_mod(self.wordline_driver) - def create_row_decoder(self): """ Create the hierarchical row decoder """ - self.row_decoder_inst = self.add_inst(name="row_decoder", + self.row_decoder_inst = self.add_inst(name="row_decoder", mod=self.row_decoder) temp = [] diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 1ccad6ca..5e44ecff 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -15,6 +15,7 @@ from globals import OPTS from utils import round_to_grid import logical_effort from sram_factory import factory +from errors import drc_error class pinv(pgate.pgate): @@ -108,11 +109,14 @@ class pinv(pgate.pgate): # 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, self.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)) + 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)) + if total_height > self.height: + msg = "Cell height {0} too small for simple min height {1}.".format(self.height, total_height) + raise drc_error(msg) # Determine the height left to the transistors to determine # the number of fingers @@ -144,12 +148,16 @@ class pinv(pgate.pgate): # 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.") - 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.nmos_width >= drc("minwidth_tx"), + # "Cannot finger NMOS transistors to fit cell height.") + if self.nmos_width < drc("minwidth_tx"): + raise drc_error("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.") + if self.pmos_width < drc("minwidth_tx"): + raise drc_error("Cannot finger NMOS transistors to fit cell height.") def add_ptx(self): """ Create the PMOS and NMOS transistors. """ diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 22a03b6d..a2dc7d0b 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -103,7 +103,7 @@ class pnand2(pgate.pgate): # metal spacing to allow contacts on any layer self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width, self.m1_space + contact.m1_via.first_layer_width, - self.m2_space + contact.m2_via.first_layer_width, + self.m2_space + contact.m2_via.first_layer_width, self.m3_space + contact.m2_via.second_layer_width) @@ -178,13 +178,15 @@ class pnand2(pgate.pgate): 0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos_nd.active_height)) def add_well_contacts(self): - """ + """ Add n/p well taps to the layout and connect to supplies AFTER the wells are created """ - self.add_nwell_contact(self.pmos, self.pmos2_pos) - self.add_pwell_contact(self.nmos_nd, self.nmos2_pos) + self.add_nwell_contact(self.pmos, + self.pmos2_pos + vector(self.m1_pitch, 0)) + self.add_pwell_contact(self.nmos_nd, + self.nmos2_pos + vector(self.m1_pitch, 0)) def connect_rails(self): """ Connect the nmos and pmos to its respective power rails """ @@ -202,7 +204,7 @@ class pnand2(pgate.pgate): self.nmos2_inst, inputB_yoffset, "B", - position="right") + position="center") # This will help with the wells and the input/output placement self.inputA_yoffset = self.pmos2_inst.by() - self.poly_extend_active \ @@ -214,6 +216,7 @@ class pnand2(pgate.pgate): def route_output(self): """ Route the Z output """ + # PMOS1 drain pmos_pin = self.pmos1_inst.get_pin("D") top_pin_offset = pmos_pin.center() @@ -222,29 +225,46 @@ class pnand2(pgate.pgate): bottom_pin_offset = nmos_pin.center() # Output pin - out_offset = vector(nmos_pin.center().x + self.m1_pitch, + c_pin = self.get_pin("B") + out_offset = vector(c_pin.cx() + self.m1_pitch, self.inputA_yoffset) - # Midpoints of the L routes go horizontal first then vertical - mid1_offset = vector(out_offset.x, top_pin_offset.y) + # This routes on M2 + # # Midpoints of the L routes go horizontal first then vertical + # mid1_offset = vector(out_offset.x, top_pin_offset.y) + # mid2_offset = vector(out_offset.x, bottom_pin_offset.y) + + # # Non-preferred active contacts + # self.add_via_center(layers=self.m1_stack, + # directions=("V", "H"), + # offset=pmos_pin.center()) + # # Non-preferred active contacts + # self.add_via_center(layers=self.m1_stack, + # directions=("V", "H"), + # offset=nmos_pin.center()) + # self.add_via_center(layers=self.m1_stack, + # offset=out_offset) + + # # PMOS1 to mid-drain to NMOS2 drain + # self.add_path("m2", + # [top_pin_offset, mid1_offset, out_offset, + # mid2_offset, bottom_pin_offset]) + + # This routes on M1 + # Midpoints of the L routes goes vertical first then horizontal + mid1_offset = vector(top_pin_offset.x, out_offset.y) + # Midpoints of the L routes goes horizontal first then vertical mid2_offset = vector(out_offset.x, bottom_pin_offset.y) - # Non-preferred active contacts - self.add_via_center(layers=self.m1_stack, - directions=("V", "H"), - offset=pmos_pin.center()) - # Non-preferred active contacts - self.add_via_center(layers=self.m1_stack, - directions=("V", "H"), - offset=nmos_pin.center()) - self.add_via_center(layers=self.m1_stack, - offset=out_offset) + self.add_path("m1", + [top_pin_offset, mid1_offset, out_offset]) + # Route in two segments to have the width rule + self.add_path("m1", + [bottom_pin_offset, mid2_offset + vector(0.5 * self.m1_width, 0)], + width=nmos_pin.height()) + self.add_path("m1", + [mid2_offset, out_offset]) - # PMOS1 to mid-drain to NMOS2 drain - self.add_path("m2", - [top_pin_offset, mid1_offset, out_offset, - mid2_offset, bottom_pin_offset]) - # This extends the output to the edge of the cell self.add_layout_pin_rect_center(text="Z", layer="m1", diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index c93c867f..866d628a 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -194,8 +194,10 @@ class pnand3(pgate.pgate): def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ - self.add_nwell_contact(self.pmos, self.pmos3_pos) - self.add_pwell_contact(self.nmos_ns, self.nmos3_pos) + self.add_nwell_contact(self.pmos, + self.pmos3_pos + vector(self.m1_pitch, 0)) + self.add_pwell_contact(self.nmos_ns, + self.nmos3_pos + vector(self.m1_pitch, 0)) def connect_rails(self): """ Connect the nmos and pmos to its respective power rails """ @@ -224,18 +226,22 @@ class pnand3(pgate.pgate): self.nmos3_inst, self.inputC_yoffset, "C", - position="center") + position="right") # FIXME: constant hack - self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch + if OPTS.tech_name == "s8": + self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch + else: + self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A", - position="center") + position="left") def route_output(self): """ Route the Z output """ + # PMOS1 drain pmos1_pin = self.pmos1_inst.get_pin("D") # PMOS3 drain @@ -243,29 +249,56 @@ class pnand3(pgate.pgate): # NMOS3 drain nmos3_pin = self.nmos3_inst.get_pin("D") - # Go up to metal2 for ease on all output pins - self.add_via_center(layers=self.m1_stack, - offset=pmos1_pin.center(), - directions=("V", "V")) - self.add_via_center(layers=self.m1_stack, - offset=pmos3_pin.center(), - directions=("V", "V")) - self.add_via_center(layers=self.m1_stack, - offset=nmos3_pin.center(), - directions=("V", "V")) + # midpoint for routing + mid_offset = vector(nmos3_pin.cx() + self.m1_pitch, + self.inputA_yoffset) + + # Aligned with the well taps + out_offset = vector(self.nwell_contact.cx(), + self.inputA_yoffset) - # PMOS3 and NMOS3 are drain aligned - self.add_path("m2", [pmos3_pin.center(), nmos3_pin.center()]) - # Route in the A input track (top track) - mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) - self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) + # Go up to metal2 for ease on all output pins + # self.add_via_center(layers=self.m1_stack, + # offset=pmos1_pin.center(), + # directions=("V", "V")) + # self.add_via_center(layers=self.m1_stack, + # offset=pmos3_pin.center(), + # directions=("V", "V")) + # self.add_via_center(layers=self.m1_stack, + # offset=nmos3_pin.center(), + # directions=("V", "V")) + + # # Route in the A input track (top track) + # mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) + # self.add_path("m1", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) # This extends the output to the edge of the cell - self.add_via_center(layers=self.m1_stack, - offset=mid_offset) + # self.add_via_center(layers=self.m1_stack, + # offset=mid_offset) + + top_left_pin_offset = pmos1_pin.center() + top_right_pin_offset = pmos3_pin.center() + bottom_pin_offset = nmos3_pin.center() + + # PMOS1 to output + self.add_path("m1", [top_left_pin_offset, + vector(top_left_pin_offset.x, out_offset.y), + out_offset]) + # PMOS3 to output + self.add_path("m1", [top_right_pin_offset, + vector(top_right_pin_offset.x, mid_offset.y), + mid_offset]) + # NMOS3 to output + mid2_offset = vector(mid_offset.x, bottom_pin_offset.y) + self.add_path("m1", + [bottom_pin_offset, mid2_offset], + width=nmos3_pin.height()) + mid3_offset = vector(mid_offset.x, nmos3_pin.by()) + self.add_path("m1", [mid3_offset, mid_offset]) + self.add_layout_pin_rect_center(text="Z", layer="m1", - offset=mid_offset, + offset=out_offset, width=contact.m1_via.first_layer_width, height=contact.m1_via.first_layer_height) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index d51f42be..b4b20381 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -107,10 +107,7 @@ class ptx(design.design): # be decided in the layout later. area_sd = 2.5 * self.poly_width * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width - - if OPTS.tech_name == "s8": - print("here {0}".format(self.name)) # s8 technology is in microns main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], self.mults, @@ -474,13 +471,13 @@ class ptx(design.design): """Returns an object representing the parameters for delay in tau units.""" # FIXME: Using the same definition as the pinv.py. - parasitic_delay = 1 - size = self.mults*self.tx_width/drc("minwidth_tx") - return logical_effort.logical_effort(self.name, - size, - self.input_load(), - cout, - parasitic_delay) + parasitic_delay = 1 + size = self.mults * self.tx_width / drc("minwidth_tx") + return logical_effort.logical_effort(self.name, + size, + self.input_load(), + cout, + parasitic_delay) def input_load(self): """ @@ -488,7 +485,7 @@ class ptx(design.design): """ # FIXME: this will be applied for the loads of the drain/source - return self.mults*self.tx_width/drc("minwidth_tx") + return self.mults * self.tx_width / drc("minwidth_tx") def add_diff_contact(self, label, pos): contact=self.add_via_center(layers=self.active_stack, @@ -499,14 +496,25 @@ class ptx(design.design): well_type=self.well_type) if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=pos) - + contact=self.add_via_center(layers=self.li_stack, + offset=pos, + directions=("V", "V")) + + # contact_area = contact.mod.second_layer_width * contact.mod.second_layer_height + # min_area = drc("minarea_m1") + # width = contact.mod.second_layer_width + # if contact_area < min_area: + # height = min_area / width + # else: + # height = contact.mod.second_layer_height + width = contact.mod.second_layer_width + height = contact.mod.second_layer_height self.add_layout_pin_rect_center(text=label, layer="m1", offset=pos, - width=contact.mod.second_layer_width, - height=contact.mod.second_layer_height) + width=width, + height=height) + return(contact) def get_cin(self): diff --git a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py index 1fb3a3f6..ec8f4efe 100755 --- a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py @@ -28,39 +28,39 @@ class hierarchical_decoder_test(openram_test): factory.reset() debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=16) + a = factory.create(module_type="hierarchical_decoder", num_outputs=16) self.local_check(a) factory.reset() debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=17) + a = factory.create(module_type="hierarchical_decoder", num_outputs=17) self.local_check(a) factory.reset() debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=23) + a = factory.create(module_type="hierarchical_decoder", num_outputs=23) self.local_check(a) debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=32) + a = factory.create(module_type="hierarchical_decoder", num_outputs=32) self.local_check(a) factory.reset() debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=65) + a = factory.create(module_type="hierarchical_decoder", num_outputs=65) self.local_check(a) debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=128) + a = factory.create(module_type="hierarchical_decoder", num_outputs=128) self.local_check(a) factory.reset() debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=341) + a = factory.create(module_type="hierarchical_decoder", num_outputs=341) self.local_check(a) debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=512) + a = factory.create(module_type="hierarchical_decoder", num_outputs=512) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index ab7e844f..7b77c6d9 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -20,47 +20,38 @@ class hierarchical_decoder_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # Doesn't require hierarchical decoder - # debug.info(1, "Testing 4 row sample for hierarchical_decoder") - # a = hierarchical_decoder.hierarchical_decoder(name="hd1, rows=4) - # self.local_check(a) - - # Doesn't require hierarchical decoder - # debug.info(1, "Testing 8 row sample for hierarchical_decoder") - # a = hierarchical_decoder.hierarchical_decoder(name="hd2", rows=8) - # self.local_check(a) # check hierarchical decoder for single port debug.info(1, "Testing 16 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=16) + a = factory.create(module_type="hierarchical_decoder", num_outputs=16) self.local_check(a) debug.info(1, "Testing 17 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=17) + a = factory.create(module_type="hierarchical_decoder", num_outputs=17) self.local_check(a) debug.info(1, "Testing 23 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=23) + a = factory.create(module_type="hierarchical_decoder", num_outputs=23) self.local_check(a) debug.info(1, "Testing 32 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=32) + a = factory.create(module_type="hierarchical_decoder", num_outputs=32) self.local_check(a) debug.info(1, "Testing 65 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=65) + a = factory.create(module_type="hierarchical_decoder", num_outputs=65) self.local_check(a) debug.info(1, "Testing 128 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=128) + a = factory.create(module_type="hierarchical_decoder", num_outputs=128) self.local_check(a) debug.info(1, "Testing 341 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=341) + a = factory.create(module_type="hierarchical_decoder", num_outputs=341) self.local_check(a) debug.info(1, "Testing 512 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=512) + a = factory.create(module_type="hierarchical_decoder", num_outputs=512) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/30_openram_back_end_test.py b/compiler/tests/30_openram_back_end_test.py index 73d201b1..591d03ac 100755 --- a/compiler/tests/30_openram_back_end_test.py +++ b/compiler/tests/30_openram_back_end_test.py @@ -51,7 +51,7 @@ class openram_back_end_test(openram_test): debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") exe_name = "{0}/openram.py ".format(OPENRAM_HOME) else: - exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) + exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) config_name = "{0}/tests/configs/config_back_end.py".format(OPENRAM_HOME) cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, out_file, @@ -64,33 +64,32 @@ class openram_back_end_test(openram_test): # assert an error until we actually check a resul for extension in ["gds", "v", "lef", "sp"]: - filename = "{0}/{1}.{2}".format(out_path,out_file,extension) - debug.info(1,"Checking for file: " + filename) - self.assertEqual(os.path.exists(filename),True) + filename = "{0}/{1}.{2}".format(out_path, out_file, extension) + debug.info(1, "Checking for file: " + filename) + self.assertEqual(os.path.exists(filename), True) # Make sure there is any .lib file import glob files = glob.glob('{0}/*.lib'.format(out_path)) self.assertTrue(len(files)>0) - # Make sure there is any .html file + # Make sure there is any .html file if os.path.exists(out_path): datasheets = glob.glob('{0}/*html'.format(out_path)) self.assertTrue(len(datasheets)>0) # grep any errors from the output - output_log = open("{0}/output.log".format(out_path),"r") + output_log = open("{0}/output.log".format(out_path), "r") output = output_log.read() output_log.close() - self.assertEqual(len(re.findall('ERROR',output)),0) - self.assertEqual(len(re.findall('WARNING',output)),0) - + self.assertEqual(len(re.findall('ERROR', output)), 0) + self.assertEqual(len(re.findall('WARNING', output)), 0) # now clean up the directory if OPTS.purge_temp: if os.path.exists(out_path): shutil.rmtree(out_path, ignore_errors=True) - self.assertEqual(os.path.exists(out_path),False) + self.assertEqual(os.path.exists(out_path), False) globals.end_openram() diff --git a/compiler/tests/30_openram_front_end_test.py b/compiler/tests/30_openram_front_end_test.py index 3aec10f9..127df6c2 100755 --- a/compiler/tests/30_openram_front_end_test.py +++ b/compiler/tests/30_openram_front_end_test.py @@ -51,7 +51,7 @@ class openram_front_end_test(openram_test): debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") exe_name = "{0}/openram.py ".format(OPENRAM_HOME) else: - exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) + exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) config_name = "{0}/tests/configs/config_front_end.py".format(OPENRAM_HOME) cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, out_file, @@ -64,33 +64,32 @@ class openram_front_end_test(openram_test): # assert an error until we actually check a result for extension in ["v", "lef", "sp", "gds"]: - filename = "{0}/{1}.{2}".format(out_path,out_file,extension) - debug.info(1,"Checking for file: " + filename) - self.assertEqual(os.path.exists(filename),True) + filename = "{0}/{1}.{2}".format(out_path, out_file, extension) + debug.info(1, "Checking for file: " + filename) + self.assertEqual(os.path.exists(filename), True) # Make sure there is any .lib file import glob files = glob.glob('{0}/*.lib'.format(out_path)) self.assertTrue(len(files)>0) - # Make sure there is any .html file + # Make sure there is any .html file if os.path.exists(out_path): datasheets = glob.glob('{0}/*html'.format(out_path)) self.assertTrue(len(datasheets)>0) # grep any errors from the output - output_log = open("{0}/output.log".format(out_path),"r") + output_log = open("{0}/output.log".format(out_path), "r") output = output_log.read() output_log.close() - self.assertEqual(len(re.findall('ERROR',output)),0) - self.assertEqual(len(re.findall('WARNING',output)),0) + self.assertEqual(len(re.findall('ERROR', output)), 0) + self.assertEqual(len(re.findall('WARNING', output)), 0) - - # now clean up the directory + # now clean up the directory if OPTS.purge_temp: if os.path.exists(out_path): shutil.rmtree(out_path, ignore_errors=True) - self.assertEqual(os.path.exists(out_path),False) + self.assertEqual(os.path.exists(out_path), False) globals.end_openram()