diff --git a/.coveragerc b/.coveragerc index 5a8c6f66..a9eb6a64 100644 --- a/.coveragerc +++ b/.coveragerc @@ -4,6 +4,10 @@ omit = */.local/* # omit everything in /usr /usr/* + # ignore the unit tests themselves + */tests/* + # ignore the debug utilities + debug.py [paths] source = /home/gitlab-runner/builds/2fd64746/0 @@ -12,3 +16,13 @@ source = /home/gitlab-runner/builds/2fd64746/3 /home/gitlab-runner/builds/2fd64746/4 /home/gitlab-runner/builds/2fd64746/5 +[report] +exclude_lines = + pragma: no cover + def __repr__ + except Exception + raise AssertionError + raise NotImplementedError + if 0: + if __name__ == "__main__": + if not OPTS.is_unit_test \ No newline at end of file diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 07d4f611..9843dcef 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -673,11 +673,13 @@ class layout(lef.lef): offset=bus_pos, rotate=90) - def add_horizontal_trunk_route(self, pins, trunk_offset, + def add_horizontal_trunk_route(self, + pins, + trunk_offset, layer_stack=("metal1", "via1", "metal2"), pitch=None): """ - Create a trunk route for all pins with the the trunk located at the given y offset. + Create a trunk route for all pins with the trunk located at the given y offset. """ if not pitch: pitch = self.m1_pitch @@ -704,15 +706,18 @@ class layout(lef.lef): # Route each pin to the trunk for pin in pins: - # Bend to the center of the trunk so it adds a via automatically mid = vector(pin.center().x, trunk_offset.y) - self.add_wire(layer_stack, [pin.center(), mid, trunk_mid]) + self.add_path(layer_stack[2], [pin.center(), mid]) + self.add_via_center(layers=layer_stack, + offset=mid) - def add_vertical_trunk_route(self, pins, trunk_offset, + def add_vertical_trunk_route(self, + pins, + trunk_offset, layer_stack=("metal1", "via1", "metal2"), pitch=None): """ - Create a trunk route for all pins with the the trunk located at the given x offset. + Create a trunk route for all pins with the trunk located at the given x offset. """ if not pitch: pitch = self.m2_pitch @@ -740,18 +745,21 @@ class layout(lef.lef): # Route each pin to the trunk for pin in pins: - # Bend to the center of the trunk so it adds a via automatically mid = vector(trunk_offset.x, pin.center().y) - self.add_wire(layer_stack, [pin.center(), mid, trunk_mid]) + self.add_path(layer_stack[0], [pin.center(), mid]) + self.add_via_center(layers=layer_stack, + offset=mid, + rotate=90) - def create_channel_route(self, netlist, pins, offset, - layer_stack=("metal1", "via1", "metal2"), pitch=None, + def create_channel_route(self, netlist, + offset, + layer_stack=("metal1", "via1", "metal2"), + pitch=None, vertical=False): """ - The net list is a list of the nets. Each net is a list of pin - names to be connected. Pins is a dictionary of the pin names - to the pin structures. Offset is the lower-left of where the + The net list is a list of the nets. Each net is a list of pins + to be connected. Offset is the lower-left of where the routing channel will start. This does NOT try to minimize the number of tracks -- instead, it picks an order to avoid the vertical conflicts between pins. @@ -786,7 +794,10 @@ class layout(lef.lef): def vcg_pin_overlap(pin1, pin2, vertical): """ Check for vertical or horizontal overlap of the two pins """ - + # FIXME: If the pins are not in a row, this may break. + # However, a top pin shouldn't overlap another top pin, for example, so the + # extra comparison *shouldn't* matter. + # Pin 1 must be in the "BOTTOM" set x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x-pin2.center().x) 0.88 * self.vdd_voltage: + if value > self.v_high: sp_read_value = "1" + sp_read_value - elif value < 0.12 * self.vdd_voltage: + elif value < self.v_low: sp_read_value = "0" + sp_read_value else: error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port, bit, value, eo_period, - 0.12*self.vdd_voltage, - 0.88*self.vdd_voltage) + self.v_low, + self.v_high) return (0, error) self.read_check.append([sp_read_value, dout_port, eo_period, check]) diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index 46bd6c57..beca0502 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -37,6 +37,9 @@ class simulation(): self.period = tech.spice["feasible_period"] self.slew = tech.spice["rise_time"]*2 self.load = tech.spice["msflop_in_cap"]*4 + + self.v_high = self.vdd_voltage - tech.spice["v_threshold_typical"] + self.v_low = tech.spice["v_threshold_typical"] self.gnd_voltage = 0 def set_stimulus_variables(self): diff --git a/compiler/example_config_scn4m_subm.py b/compiler/example_config_scn4m_subm.py index 5b97e0eb..7fafeb08 100644 --- a/compiler/example_config_scn4m_subm.py +++ b/compiler/example_config_scn4m_subm.py @@ -3,8 +3,8 @@ num_words = 16 tech_name = "scn4m_subm" process_corners = ["TT"] -supply_voltages = [ 3.3 ] -temperatures = [ 25 ] +supply_voltages = [5.0] +temperatures = [25] output_path = "temp" output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index c4f4d557..150b05af 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -10,7 +10,6 @@ from pinv import pinv from pnand2 import pnand2 from pnor2 import pnor2 from vector import vector -from pinvbuf import pinvbuf from globals import OPTS @@ -85,11 +84,12 @@ class bank(design.design): self.add_pin("bank_sel{}".format(port),"INPUT") for port in self.read_ports: self.add_pin("s_en{0}".format(port), "INPUT") + for port in self.read_ports: + self.add_pin("p_en_bar{0}".format(port), "INPUT") for port in self.write_ports: self.add_pin("w_en{0}".format(port), "INPUT") for port in self.all_ports: - self.add_pin("clk_buf_bar{0}".format(port),"INPUT") - self.add_pin("clk_buf{0}".format(port),"INPUT") + self.add_pin("wl_en{0}".format(port), "INPUT") self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") @@ -226,16 +226,19 @@ class bank(design.design): # UPPER LEFT QUADRANT # To the left of the bitcell array # The wordline driver is placed to the right of the main decoder width. - x_offset = self.central_bus_width + self.wordline_driver.width + x_offset = self.m2_gap + self.wordline_driver.width self.wordline_driver_offsets[port] = vector(-x_offset,0) x_offset += self.row_decoder.width + self.m2_gap self.row_decoder_offsets[port] = vector(-x_offset,0) # LOWER LEFT QUADRANT + # Place the col decoder left aligned with wordline driver plus halfway under row decoder # Place the col decoder left aligned with row decoder (x_offset doesn't change) # Below the bitcell array with well spacing + x_offset = self.central_bus_width[port] + self.wordline_driver.width if self.col_addr_size > 0: - y_offset = self.column_decoder.height + x_offset += self.column_decoder.width + self.col_addr_bus_width + y_offset = self.m2_gap + self.column_decoder.height else: y_offset = 0 y_offset += 2*drc("well_to_well") @@ -283,16 +286,18 @@ class bank(design.design): # LOWER RIGHT QUADRANT # To the left of the bitcell array # The wordline driver is placed to the right of the main decoder width. - x_offset = self.bitcell_array.width + self.central_bus_width + self.wordline_driver.width + x_offset = self.bitcell_array.width + self.m2_gap + self.wordline_driver.width self.wordline_driver_offsets[port] = vector(x_offset,0) x_offset += self.row_decoder.width + self.m2_gap self.row_decoder_offsets[port] = vector(x_offset,0) # UPPER RIGHT QUADRANT - # Place the col decoder right aligned with row decoder (x_offset doesn't change) + # Place the col decoder right aligned with wordline driver plus halfway under row decoder # Above the bitcell array with a well spacing + x_offset = self.bitcell_array.width + self.central_bus_width[port] + self.wordline_driver.width if self.col_addr_size > 0: - y_offset = self.bitcell_array.height + self.column_decoder.height + x_offset += self.column_decoder.width + self.col_addr_bus_width + y_offset = self.bitcell_array.height + self.column_decoder.height + self.m2_gap else: y_offset = self.bitcell_array.height y_offset += 2*drc("well_to_well") @@ -349,21 +354,25 @@ class bank(design.design): # FIXME: This spacing should be width dependent... self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space - # Number of control lines in the bus - self.num_control_lines = 4 # The order of the control signals on the control bus: self.input_control_signals = [] port_num = 0 for port in range(OPTS.num_rw_ports): - self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num)]) + self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)]) port_num += 1 for port in range(OPTS.num_w_ports): - self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "w_en{}".format(port_num)]) + self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num)]) port_num += 1 for port in range(OPTS.num_r_ports): - self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "s_en{}".format(port_num)]) + self.input_control_signals.append(["wl_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)]) port_num += 1 + # Number of control lines in the bus for each port + self.num_control_lines = [len(x) for x in self.input_control_signals] + + # The width of this bus is needed to place other modules (e.g. decoder) for each port + self.central_bus_width = [self.m2_pitch*x + self.m2_width for x in self.num_control_lines] + # These will be outputs of the gaters if this is multibank, if not, normal signals. self.control_signals = [] for port in self.all_ports: @@ -371,15 +380,13 @@ class bank(design.design): self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]]) else: self.control_signals.append(self.input_control_signals[port]) + # The central bus is the column address (one hot) and row address (binary) if self.col_addr_size>0: self.num_col_addr_lines = 2**self.col_addr_size else: self.num_col_addr_lines = 0 - - # The width of this bus is needed to place other modules (e.g. decoder) - # A width on each side too - self.central_bus_width = self.m2_pitch * self.num_control_lines + self.m2_width + self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines # A space for wells or jogging m2 self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), @@ -401,16 +408,15 @@ class bank(design.design): setattr (self, "mod_"+mod_name, mod_class) - self.bitcell = self.mod_bitcell() self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols, rows=self.num_rows) self.add_mod(self.bitcell_array) # create arrays of bitline and bitline_bar names for read, write, or all ports + self.bitcell = self.mod_bitcell() self.bl_names = self.bitcell.list_all_bl_names() self.br_names = self.bitcell.list_all_br_names() - self.wl_names = self.bitcell.list_all_wl_names() self.bitline_names = self.bitcell.list_all_bitline_names() @@ -489,7 +495,7 @@ class bank(design.design): for i in range(self.num_cols): temp.append(self.bl_names[port]+"_{0}".format(i)) temp.append(self.br_names[port]+"_{0}".format(i)) - temp.extend([self.prefix+"clk_buf_bar{0}".format(port), "vdd"]) + temp.extend([self.prefix+"p_en_bar{0}".format(port), "vdd"]) self.connect_inst(temp) @@ -664,7 +670,7 @@ class bank(design.design): temp.append("dec_out{0}_{1}".format(port,row)) for row in range(self.num_rows): temp.append(self.wl_names[port]+"_{0}".format(row)) - temp.append(self.prefix+"clk_buf{0}".format(port)) + temp.append(self.prefix+"wl_en{0}".format(port)) temp.append("vdd") temp.append("gnd") self.connect_inst(temp) @@ -691,16 +697,19 @@ class bank(design.design): if self.col_addr_size == 0: return elif self.col_addr_size == 1: + from pinvbuf import pinvbuf self.column_decoder = pinvbuf(height=self.mod_dff.height) - self.add_mod(self.column_decoder) elif self.col_addr_size == 2: - self.column_decoder = self.row_decoder.pre2_4 + from hierarchical_predecode2x4 import hierarchical_predecode2x4 as pre2x4 + self.column_decoder = pre2x4(height=self.mod_dff.height) elif self.col_addr_size == 3: - self.column_decoder = self.row_decoder.pre3_8 + from hierarchical_predecode3x8 import hierarchical_predecode3x8 as pre3x8 + self.column_decoder = pre3x8(height=self.mod_dff.height) else: # No error checking before? debug.error("Invalid column decoder?",-1) - + self.add_mod(self.column_decoder) + self.column_decoder_inst = [None]*len(self.all_ports) for port in self.all_ports: self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port), @@ -774,14 +783,14 @@ class bank(design.design): """ Route the bank select logic. """ if self.port_id[port] == "rw": - bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"] - gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"] + bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"] + gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"] elif self.port_id[port] == "w": - bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "bank_sel"] - gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en"] + bank_sel_signals = ["clk_buf", "w_en", "bank_sel"] + gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en"] else: - bank_sel_signals = ["clk_buf", "clk_buf_bar", "s_en", "bank_sel"] - gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_s_en"] + bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"] + gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"] copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)] for signal in range(len(copy_control_signals)): @@ -835,8 +844,9 @@ class bank(design.design): # Port 0 # The bank is at (0,0), so this is to the left of the y-axis. # 2 pitches on the right for vias/jogs to access the inputs - control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset) - control_bus_length = self.max_y_offset - self.min_y_offset + control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_width, self.min_y_offset) + # The control bus is routed up to two pitches below the bitcell array + control_bus_length = -2*self.m1_pitch - self.min_y_offset self.bus_xoffset[0] = self.create_bus(layer="metal2", pitch=self.m2_pitch, offset=control_bus_offset, @@ -847,7 +857,11 @@ class bank(design.design): # Port 1 if len(self.all_ports)==2: - control_bus_offset = vector(self.bitcell_array.width + self.m2_width, self.min_y_offset) + # The other control bus is routed up to two pitches above the bitcell array + control_bus_length = self.max_y_offset - self.bitcell_array.height - 2*self.m1_pitch + control_bus_offset = vector(self.bitcell_array.width + self.m2_width, + self.max_y_offset - control_bus_length) + self.bus_xoffset[1] = self.create_bus(layer="metal2", pitch=self.m2_pitch, offset=control_bus_offset, @@ -891,6 +905,9 @@ class bank(design.design): inst1 = self.bitcell_array_inst inst1_bl_name = self.bl_names[port]+"_{}" inst1_br_name = self.br_names[port]+"_{}" + + # The column mux is constructed to match the bitline pitch, so we can directly connect + # here and not channel route the bitlines. self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols, inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) @@ -911,8 +928,8 @@ class bank(design.design): inst1_bl_name = "bl_{}" inst1_br_name = "br_{}" - self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, - inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) + self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, + inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) def route_write_driver_to_column_mux_or_bitcell_array(self, port): """ Routing of BL and BR between sense_amp and column mux or bitcell array """ @@ -937,7 +954,11 @@ class bank(design.design): inst1 = self.write_driver_array_inst[port] inst2 = self.sense_amp_array_inst[port] - self.connect_bitlines(inst1, inst2, self.word_size) + + # These should be pitch matched in the cell library, + # but just in case, do a channel route. + self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size) + def route_sense_amp_out(self, port): @@ -992,14 +1013,10 @@ class bank(design.design): # of tracks in teh channel router yet. If we did, we could route all the bits at once! offset = bottom_inst.ul() + vector(0,self.m1_pitch) for bit in range(num_bits): - bottom_names = [bottom_bl_name.format(bit), bottom_br_name.format(bit)] - top_names = [top_bl_name.format(bit), top_br_name.format(bit)] + bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))] + top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))] route_map = list(zip(bottom_names, top_names)) - bottom_pins = {key: bottom_inst.get_pin(key) for key in bottom_names } - top_pins = {key: top_inst.get_pin(key) for key in top_names } - all_pins = {**bottom_pins, **top_pins} - debug.check(len(all_pins)==len(bottom_pins)+len(top_pins),"Duplicate named pins in bitline channel route.") - self.create_horizontal_channel_route(route_map, all_pins, offset) + self.create_horizontal_channel_route(route_map, offset) def connect_bitlines(self, inst1, inst2, num_bits, @@ -1078,79 +1095,41 @@ class bank(design.design): self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) def route_column_address_lines(self, port): + """ Connecting the select lines of column mux to the address bus """ + if not self.col_addr_size>0: + return + + if self.col_addr_size == 1: + + # Connect to sel[0] and sel[1] + decode_names = ["Zb", "Z"] + + # The Address LSB + self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port)) + + elif self.col_addr_size > 1: + decode_names = [] + for i in range(self.num_col_addr_lines): + decode_names.append("out_{}".format(i)) + + for i in range(self.col_addr_size): + decoder_name = "in_{}".format(i) + addr_name = "addr{0}_{1}".format(port,i) + self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) + if port%2: - self.route_column_address_lines_right(port) + offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0) else: - self.route_column_address_lines_left(port) + offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0) - def route_column_address_lines_left(self, port): - """ Connecting the select lines of column mux to the address bus """ - if not self.col_addr_size>0: - return - - if self.col_addr_size == 1: - - # Connect to sel[0] and sel[1] - decode_names = ["Zb", "Z"] - - # The Address LSB - self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port)) - - elif self.col_addr_size > 1: - decode_names = [] - for i in range(self.num_col_addr_lines): - decode_names.append("out_{}".format(i)) - - for i in range(self.col_addr_size): - decoder_name = "in_{}".format(i) - addr_name = "addr{0}_{1}".format(port,i) - self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) - - offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0) - - sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] - - route_map = list(zip(decode_names, sel_names)) - decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names } - column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names } - # Combine the dff and bank pins into a single dictionary of pin name to pin. - all_pins = {**decode_pins, **column_mux_pins} - self.create_vertical_channel_route(route_map, all_pins, offset) - - def route_column_address_lines_right(self, port): - """ Connecting the select lines of column mux to the address bus """ - if not self.col_addr_size>0: - return - - if self.col_addr_size == 1: - - # Connect to sel[0] and sel[1] - decode_names = ["Zb", "Z"] - - # The Address LSB - self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port)) - - elif self.col_addr_size > 1: - decode_names = [] - for i in range(self.num_col_addr_lines): - decode_names.append("out_{}".format(i)) - - for i in range(self.col_addr_size): - decoder_name = "in_{}".format(i) - addr_name = "addr{0}_{1}".format(port,i) - self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) - - offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0) - - sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] - - route_map = list(zip(decode_names, sel_names)) - decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names } - column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names } - # Combine the dff and bank pins into a single dictionary of pin name to pin. - all_pins = {**decode_pins, **column_mux_pins} - self.create_vertical_channel_route(route_map, all_pins, offset) + decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names] + sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] + column_mux_pins = [self.column_mux_array_inst[port].get_pin(x) for x in sel_names] + + route_map = list(zip(decode_pins, column_mux_pins)) + self.create_vertical_channel_route(route_map, offset) + def add_lvs_correspondence_points(self): """ This adds some points for easier debugging if LVS goes wrong. @@ -1209,7 +1188,7 @@ class bank(design.design): connection = [] if port in self.read_ports: - connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[port].get_pin("en").lc())) + connection.append((self.prefix+"p_en_bar{}".format(port), self.precharge_array_inst[port].get_pin("en_bar").lc())) if port in self.write_ports: connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc())) @@ -1225,9 +1204,13 @@ class bank(design.design): rotate=90) # clk to wordline_driver - control_signal = self.prefix+"clk_buf{}".format(port) - pin_pos = self.wordline_driver_inst[port].get_pin("en").bc() - mid_pos = pin_pos - vector(0,self.m1_pitch) + control_signal = self.prefix+"wl_en{}".format(port) + if port%2: + pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").uc() + mid_pos = pin_pos + vector(0,self.m2_gap) # to route down to the top of the bus + else: + pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").bc() + mid_pos = pin_pos - vector(0,self.m2_gap) # to route down to the top of the bus control_x_offset = self.bus_xoffset[port][control_signal].x control_pos = vector(control_x_offset, mid_pos.y) self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index d42c134e..792fd9ae 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -132,40 +132,14 @@ class bitcell_array(design.design): # increments to the next row height offset.y += self.cell.height - # For every second row and column, add a via for vdd + # For every second row and column, add a via for gnd and vdd for row in range(self.row_size): for col in range(self.column_size): inst = self.cell_inst[row,col] - for vdd_pin in inst.get_pins("vdd"): - # Drop to M1 if needed - if vdd_pin.layer == "metal1": - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=vdd_pin.center(), - rotate=90) - # Always drop to M2 - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=vdd_pin.center()) - self.add_layout_pin_rect_center(text="vdd", - layer="metal3", - offset=vdd_pin.center()) - - - # For every second row and column (+1), add a via for gnd - for row in range(self.row_size): - for col in range(self.column_size): - inst = self.cell_inst[row,col] - for gnd_pin in inst.get_pins("gnd"): - # Drop to M1 if needed - if gnd_pin.layer == "metal1": - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=gnd_pin.center(), - rotate=90) - # Always drop to M2 - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=gnd_pin.center()) - self.add_layout_pin_rect_center(text="gnd", - layer="metal3", - offset=gnd_pin.center()) + for pin_name in ["vdd", "gnd"]: + for pin in inst.get_pins(pin_name): + self.add_power_pin(pin_name, pin.center(), 0, pin.layer) + def analytical_delay(self, slew, load=0): from tech import drc diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index d227dfce..41fd2b16 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -4,11 +4,12 @@ from tech import drc, parameter import debug import contact from pinv import pinv +from pbuf import pbuf +from pand2 import pand2 from pnand2 import pnand2 -from pnand3 import pnand3 from pinvbuf import pinvbuf -from dff_inv import dff_inv -from dff_inv_array import dff_inv_array +from dff_buf import dff_buf +from dff_buf_array import dff_buf_array import math from vector import vector from globals import OPTS @@ -47,9 +48,7 @@ class control_logic(design.design): """ Create layout and route between modules """ self.place_instances() self.route_all() - #self.add_lvs_correspondence_points() - self.DRC_LVS() @@ -65,28 +64,37 @@ class control_logic(design.design): def add_modules(self): """ Add all the required modules """ - dff = dff_inv() + dff = dff_buf() dff_height = dff.height - self.ctrl_dff_array = dff_inv_array(rows=self.num_control_signals,columns=1) + self.ctrl_dff_array = dff_buf_array(rows=self.num_control_signals,columns=1) self.add_mod(self.ctrl_dff_array) - self.nand2 = pnand2(height=dff_height) - self.add_mod(self.nand2) - self.nand3 = pnand3(height=dff_height) - self.add_mod(self.nand3) - + self.and2 = pand2(size=4,height=dff_height) + self.add_mod(self.and2) + # Special gates: inverters for buffering # Size the clock for the number of rows (fanout) clock_driver_size = max(1,int(self.num_rows/4)) - self.clkbuf = pinvbuf(clock_driver_size,height=dff_height) + self.clkbuf = pbuf(size=clock_driver_size, height=dff_height) self.add_mod(self.clkbuf) + + self.buf16 = pbuf(size=16, height=dff_height) + self.add_mod(self.buf16) + + self.buf8 = pbuf(size=8, height=dff_height) + self.add_mod(self.buf8) + self.inv = self.inv1 = pinv(size=1, height=dff_height) self.add_mod(self.inv1) - self.inv2 = pinv(size=4, height=dff_height) - self.add_mod(self.inv2) - self.inv8 = pinv(size=16, height=dff_height) + + self.inv8 = pinv(size=8, height=dff_height) self.add_mod(self.inv8) + + # self.inv2 = pinv(size=4, height=dff_height) + # self.add_mod(self.inv2) + #self.inv16 = pinv(size=16, height=dff_height) + #self.add_mod(self.inv16) if (self.port_type == "rw") or (self.port_type == "r"): from importlib import reload @@ -127,20 +135,22 @@ class control_logic(design.design): # list of output control signals (for making a vertical bus) if self.port_type == "rw": - self.internal_bus_list = ["clk_buf", "clk_buf_bar", "we", "cs"] + self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "we", "clk_buf", "we_bar", "cs"] + elif self.port_type == "r": + self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"] else: - self.internal_bus_list = ["clk_buf", "clk_buf_bar", "cs"] + self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"] # leave space for the bus plus one extra space self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch # Outputs to the bank - if self.port_type == "r": - self.output_list = ["s_en"] - elif self.port_type == "w": - self.output_list = ["w_en"] + if self.port_type == "rw": + self.output_list = ["s_en", "w_en", "p_en_bar"] + elif self.port_type == "r": + self.output_list = ["s_en", "p_en_bar"] else: - self.output_list = ["s_en", "w_en"] - self.output_list.append("clk_buf_bar") + self.output_list = ["w_en"] + self.output_list.append("wl_en") self.output_list.append("clk_buf") self.supply_list = ["vdd", "gnd"] @@ -157,11 +167,16 @@ class control_logic(design.design): def create_instances(self): """ Create all the instances """ self.create_dffs() - self.create_clk_row() + self.create_clk_buf_row() + self.create_gated_clk_bar_row() + self.create_gated_clk_buf_row() + self.create_wlen_row() if (self.port_type == "rw") or (self.port_type == "w"): - self.create_we_row() - if (self.port_type == "rw") or (self.port_type == "r"): + self.create_wen_row() + if self.port_type == "rw": self.create_rbl_in_row() + if (self.port_type == "rw") or (self.port_type == "r"): + self.create_pen_row() self.create_sen_row() self.create_rbl() @@ -175,19 +190,33 @@ class control_logic(design.design): # Add the control flops on the left of the bus self.place_dffs() + # All of the control logic is placed to the right of the DFFs and bus + self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + row = 0 # Add the logic on the right of the bus - self.place_clk_row(row=row) # clk is a double-high cell - row += 2 + self.place_clk_buf_row(row) + row += 1 + self.place_gated_clk_bar_row(row) + row += 1 + self.place_gated_clk_buf_row(row) + row += 1 + self.place_wlen_row(row) + row += 1 if (self.port_type == "rw") or (self.port_type == "w"): - self.place_we_row(row=row) + self.place_wen_row(row) height = self.w_en_inst.uy() control_center_y = self.w_en_inst.uy() row += 1 - if (self.port_type == "rw") or (self.port_type == "r"): - self.place_rbl_in_row(row=row) - self.place_sen_row(row=row+1) - self.place_rbl(row=row+2) + if self.port_type == "rw": + self.place_rbl_in_row(row) + row += 1 + if (self.port_type == "rw") or (self.port_type == "r"): + self.place_pen_row(row) + row += 1 + self.place_sen_row(row) + row += 1 + self.place_rbl(row) height = self.rbl_inst.uy() control_center_y = self.rbl_inst.by() @@ -197,122 +226,350 @@ class control_logic(design.design): # Extra pitch on top and right self.height = height + 2*self.m1_pitch # Max of modules or logic rows + self.width = max([inst.rx() for inst in self.row_end_inst]) if (self.port_type == "rw") or (self.port_type == "r"): - self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch - else: - self.width = max([inst.rx() for inst in self.row_end_inst]) + self.m2_pitch + self.width = max(self.rbl_inst.rx() , self.width) + self.width += self.m2_pitch def route_all(self): """ Routing between modules """ self.route_rails() self.route_dffs() + self.route_wlen() if (self.port_type == "rw") or (self.port_type == "w"): self.route_wen() if (self.port_type == "rw") or (self.port_type == "r"): self.route_rbl_in() + self.route_pen() self.route_sen() - self.route_clk() + self.route_clk_buf() + self.route_gated_clk_bar() + self.route_gated_clk_buf() self.route_supply() def create_rbl(self): """ Create the replica bitline """ + if self.port_type == "r": + input_name = "gated_clk_bar" + else: + input_name = "rbl_in" self.rbl_inst=self.add_inst(name="replica_bitline", mod=self.replica_bitline) - self.connect_inst(["rbl_in", "pre_s_en", "vdd", "gnd"]) + self.connect_inst([input_name, "pre_s_en", "vdd", "gnd"]) def place_rbl(self,row): """ Place the replica bitline """ - y_off = row * self.inv1.height + 2*self.m1_pitch + y_off = row * self.and2.height + 2*self.m1_pitch # Add the RBL above the rows # Add to the right of the control rows and routing channel - self.replica_bitline_offset = vector(0, y_off) - self.rbl_inst.place(self.replica_bitline_offset) + offset = vector(0, y_off) + self.rbl_inst.place(offset) - def create_clk_row(self): - """ Create the multistage clock buffer """ + def create_clk_buf_row(self): + """ Create the multistage and gated clock buffer """ self.clkbuf_inst = self.add_inst(name="clkbuf", mod=self.clkbuf) - self.connect_inst(["clk","clk_buf_bar","clk_buf","vdd","gnd"]) - - def place_clk_row(self,row): - """ Place the multistage clock buffer below the control flops """ - x_off = self.ctrl_dff_array.width + self.internal_bus_width - (y_off,mirror)=self.get_offset(row) - clkbuf_offset = vector(x_off,y_off) - self.clkbuf_inst.place(clkbuf_offset) - self.row_end_inst.append(self.clkbuf_inst) + self.connect_inst(["clk","clk_buf","vdd","gnd"]) + def place_clk_buf_row(self,row): + """ Place the multistage clock buffer below the control flops """ + x_off = self.control_x_offset + (y_off,mirror)=self.get_offset(row) + + offset = vector(x_off,y_off) + self.clkbuf_inst.place(offset, mirror) + + self.row_end_inst.append(self.clkbuf_inst) + + def route_clk_buf(self): + clk_pin = self.clkbuf_inst.get_pin("A") + clk_pos = clk_pin.center() + self.add_layout_pin_segment_center(text="clk", + layer="metal2", + start=clk_pos, + end=clk_pos.scale(1,0)) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=clk_pos) + + + clkbuf_map = zip(["Z"], ["clk_buf"]) + self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + # The pin is on M1, so we need another via as well + self.add_via_center(layers=("metal1","via1","metal2"), + offset=self.clkbuf_inst.get_pin("Z").center()) + + + self.connect_output(self.clkbuf_inst, "Z", "clk_buf") + + def create_gated_clk_bar_row(self): + self.clk_bar_inst = self.add_inst(name="inv_clk_bar", + mod=self.inv) + self.connect_inst(["clk_buf","clk_bar","vdd","gnd"]) + + self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar", + mod=self.and2) + self.connect_inst(["cs","clk_bar","gated_clk_bar","vdd","gnd"]) + + def place_gated_clk_bar_row(self,row): + """ Place the gated clk logic below the control flops """ + x_off = self.control_x_offset + (y_off,mirror)=self.get_offset(row) + + offset = vector(x_off,y_off) + self.clk_bar_inst.place(offset, mirror) + + x_off += self.inv.width + + offset = vector(x_off,y_off) + self.gated_clk_bar_inst.place(offset, mirror) + + self.row_end_inst.append(self.gated_clk_bar_inst) + + def route_gated_clk_bar(self): + clkbuf_map = zip(["A"], ["clk_buf"]) + self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets) + + out_pos = self.clk_bar_inst.get_pin("Z").center() + in_pos = self.gated_clk_bar_inst.get_pin("B").center() + mid1 = vector(in_pos.x,out_pos.y) + self.add_path("metal1",[out_pos, mid1, in_pos]) + + # This is the second gate over, so it needs to be on M3 + clkbuf_map = zip(["A"], ["cs"]) + self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + # The pin is on M1, so we need another via as well + self.add_via_center(layers=("metal1","via1","metal2"), + offset=self.gated_clk_bar_inst.get_pin("A").center()) + + + # This is the second gate over, so it needs to be on M3 + clkbuf_map = zip(["Z"], ["gated_clk_bar"]) + self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + # The pin is on M1, so we need another via as well + self.add_via_center(layers=("metal1","via1","metal2"), + offset=self.gated_clk_bar_inst.get_pin("Z").center()) + + def create_gated_clk_buf_row(self): + self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf", + mod=self.and2) + self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"]) + + def place_gated_clk_buf_row(self,row): + """ Place the gated clk logic below the control flops """ + x_off = self.control_x_offset + (y_off,mirror)=self.get_offset(row) + + offset = vector(x_off,y_off) + self.gated_clk_buf_inst.place(offset, mirror) + + self.row_end_inst.append(self.gated_clk_buf_inst) + + def route_gated_clk_buf(self): + clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"]) + self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets) + + + clkbuf_map = zip(["Z"], ["gated_clk_buf"]) + self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + # The pin is on M1, so we need another via as well + self.add_via_center(layers=("metal1","via1","metal2"), + offset=self.gated_clk_buf_inst.get_pin("Z").center()) + + def create_wlen_row(self): + # input pre_p_en, output: wl_en + self.wl_en_inst=self.add_inst(name="buf_wl_en", + mod=self.buf16) + self.connect_inst(["gated_clk_bar", "wl_en", "vdd", "gnd"]) + + def place_wlen_row(self, row): + x_off = self.control_x_offset + (y_off,mirror)=self.get_offset(row) + + offset = vector(x_off, y_off) + self.wl_en_inst.place(offset, mirror) + + self.row_end_inst.append(self.wl_en_inst) + + def route_wlen(self): + wlen_map = zip(["A"], ["gated_clk_bar"]) + self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.rail_offsets) + self.connect_output(self.wl_en_inst, "Z", "wl_en") def create_rbl_in_row(self): - self.rbl_in_bar_inst=self.add_inst(name="nand2_rbl_in_bar", - mod=self.nand2) - self.connect_inst(["clk_buf_bar", "cs", "rbl_in_bar", "vdd", "gnd"]) - - # input: rbl_in_bar, output: rbl_in - self.rbl_in_inst=self.add_inst(name="inv_rbl_in", - mod=self.inv1) - self.connect_inst(["rbl_in_bar", "rbl_in", "vdd", "gnd"]) + # input: gated_clk_bar, we_bar, output: rbl_in + self.rbl_in_inst=self.add_inst(name="and2_rbl_in", + mod=self.and2) + self.connect_inst(["gated_clk_bar", "we_bar", "rbl_in", "vdd", "gnd"]) def place_rbl_in_row(self,row): - x_off = self.ctrl_dff_array.width + self.internal_bus_width + x_off = self.control_x_offset (y_off,mirror)=self.get_offset(row) + offset = vector(x_off, y_off) + self.rbl_in_inst.place(offset, mirror) - self.rbl_in_bar_offset = vector(x_off, y_off) - self.rbl_in_bar_inst.place(offset=self.rbl_in_bar_offset, - mirror=mirror) - x_off += self.nand2.width - - self.rbl_in_offset = vector(x_off, y_off) - self.rbl_in_inst.place(offset=self.rbl_in_offset, - mirror=mirror) self.row_end_inst.append(self.rbl_in_inst) + + def route_rbl_in(self): + """ Connect the logic for the rbl_in generation """ + + if self.port_type == "rw": + input_name = "we_bar" + # Connect the NAND gate inputs to the bus + rbl_in_map = zip(["A", "B"], ["gated_clk_bar", "we_bar"]) + self.connect_vertical_bus(rbl_in_map, self.rbl_in_inst, self.rail_offsets) + + + # Connect the output of the precharge enable to the RBL input + if self.port_type == "rw": + out_pos = self.rbl_in_inst.get_pin("Z").center() + else: + out_pos = vector(self.rail_offsets["gated_clk_bar"].x, self.rbl_inst.by()-3*self.m2_pitch) + in_pos = self.rbl_inst.get_pin("en").center() + mid1 = vector(in_pos.x,out_pos.y) + self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, in_pos]) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=out_pos, + rotate=90) + self.add_via_center(layers=("metal2","via2","metal3"), + offset=out_pos, + rotate=90) + + def create_pen_row(self): + if self.port_type == "rw": + # input: gated_clk_bar, we_bar, output: pre_p_en + self.pre_p_en_inst=self.add_inst(name="and2_pre_p_en", + mod=self.and2) + self.connect_inst(["gated_clk_buf", "we_bar", "pre_p_en", "vdd", "gnd"]) + input_name = "pre_p_en" + else: + input_name = "gated_clk_buf" + + # input: pre_p_en, output: p_en_bar + self.p_en_bar_inst=self.add_inst(name="inv_p_en_bar", + mod=self.inv8) + self.connect_inst([input_name, "p_en_bar", "vdd", "gnd"]) + + + def place_pen_row(self,row): + x_off = self.control_x_offset + (y_off,mirror)=self.get_offset(row) + + if self.port_type == "rw": + offset = vector(x_off, y_off) + self.pre_p_en_inst.place(offset, mirror) + + x_off += self.and2.width + + offset = vector(x_off,y_off) + self.p_en_bar_inst.place(offset, mirror) + + self.row_end_inst.append(self.p_en_bar_inst) + + def route_pen(self): + if self.port_type == "rw": + # Connect the NAND gate inputs to the bus + pre_p_en_in_map = zip(["A", "B"], ["gated_clk_buf", "we_bar"]) + self.connect_vertical_bus(pre_p_en_in_map, self.pre_p_en_inst, self.rail_offsets) + + out_pos = self.pre_p_en_inst.get_pin("Z").center() + in_pos = self.p_en_bar_inst.get_pin("A").lc() + mid1 = vector(out_pos.x,in_pos.y) + self.add_wire(("metal1","via1","metal2"),[out_pos,mid1,in_pos]) + else: + in_map = zip(["A"], ["gated_clk_buf"]) + self.connect_vertical_bus(in_map, self.p_en_bar_inst, self.rail_offsets) + + self.connect_output(self.p_en_bar_inst, "Z", "p_en_bar") def create_sen_row(self): """ Create the sense enable buffer. """ - # input: pre_s_en, output: pre_s_en_bar - self.pre_s_en_bar_inst=self.add_inst(name="inv_pre_s_en_bar", - mod=self.inv2) - self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"]) - - # BUFFER INVERTERS FOR S_EN - # input: input: pre_s_en_bar, output: s_en - self.s_en_inst=self.add_inst(name="inv_s_en", - mod=self.inv8) - self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"]) + # BUFFER FOR S_EN + # input: pre_s_en, output: s_en + self.s_en_inst=self.add_inst(name="buf_s_en", + mod=self.buf8) + self.connect_inst(["pre_s_en", "s_en", "vdd", "gnd"]) def place_sen_row(self,row): """ The sense enable buffer gets placed to the far right of the row. """ - x_off = self.ctrl_dff_array.width + self.internal_bus_width + x_off = self.control_x_offset (y_off,mirror)=self.get_offset(row) - self.pre_s_en_bar_offset = vector(x_off, y_off) - self.pre_s_en_bar_inst.place(offset=self.pre_s_en_bar_offset, - mirror=mirror) - x_off += self.inv2.width + offset = vector(x_off, y_off) + self.s_en_inst.place(offset, mirror) - self.s_en_offset = vector(x_off, y_off) - self.s_en_inst.place(offset=self.s_en_offset, - mirror=mirror) self.row_end_inst.append(self.s_en_inst) + + def route_sen(self): + + out_pos = self.rbl_inst.get_pin("out").bc() + in_pos = self.s_en_inst.get_pin("A").lc() + mid1 = vector(out_pos.x,in_pos.y) + self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos]) + + self.connect_output(self.s_en_inst, "Z", "s_en") + + + def create_wen_row(self): + # input: we (or cs) output: w_en + if self.port_type == "rw": + input_name = "we" + else: + # No we for write-only reports, so use cs + input_name = "cs" + + # BUFFER FOR W_EN + self.w_en_inst = self.add_inst(name="buf_w_en_buf", + mod=self.buf8) + self.connect_inst([input_name, "w_en", "vdd", "gnd"]) + + + def place_wen_row(self,row): + x_off = self.ctrl_dff_inst.width + self.internal_bus_width + (y_off,mirror)=self.get_offset(row) + + offset = vector(x_off, y_off) + self.w_en_inst.place(offset, mirror) + + self.row_end_inst.append(self.w_en_inst) + + def route_wen(self): + + if self.port_type == "rw": + input_name = "we" + else: + # No we for write-only reports, so use cs + input_name = "cs" + + wen_map = zip(["A"], [input_name]) + self.connect_vertical_bus(wen_map, self.w_en_inst, self.rail_offsets) + + self.connect_output(self.w_en_inst, "Z", "w_en") + + def create_dffs(self): + self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs", + mod=self.ctrl_dff_array) + self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list) + + def place_dffs(self): + self.ctrl_dff_inst.place(vector(0,0)) def route_dffs(self): - """ Route the input inverters """ - - if self.port_type == "r": - control_inputs = ["cs"] + if self.port_type == "rw": + dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"]) + elif self.port_type == "r": + dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"]) else: - control_inputs = ["cs", "we"] - dff_out_map = zip(["dout_bar_{}".format(i) for i in range(2*self.num_control_signals - 1)], control_inputs) - self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets) + dff_out_map = zip(["dout_bar_0"], ["cs"]) + self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("metal3", "via2", "metal2")) # Connect the clock rail to the other clock rail in_pos = self.ctrl_dff_inst.get_pin("clk").uc() @@ -327,207 +584,18 @@ class control_logic(design.design): if (self.port_type == "rw"): self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web") - - def create_dffs(self): - """ Add the three input DFFs (with inverters) """ - self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs", - mod=self.ctrl_dff_array) - self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list) - - def place_dffs(self): - """ Place the input DFFs (with inverters) """ - self.ctrl_dff_inst.place(vector(0,0)) - - def get_offset(self,row): """ Compute the y-offset and mirroring """ - y_off = row*self.inv1.height + y_off = row*self.and2.height if row % 2: - y_off += self.inv1.height + y_off += self.and2.height mirror="MX" else: mirror="R0" return (y_off,mirror) - def create_we_row(self): - # input: WE, CS output: w_en_bar - if self.port_type == "rw": - nand_mod = self.nand3 - temp = ["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"] - else: - nand_mod = self.nand2 - temp = ["clk_buf_bar", "cs", "w_en_bar", "vdd", "gnd"] - - self.w_en_bar_inst = self.add_inst(name="nand3_w_en_bar", - mod=nand_mod) - self.connect_inst(temp) - - # input: w_en_bar, output: pre_w_en - self.pre_w_en_inst = self.add_inst(name="inv_pre_w_en", - mod=self.inv1) - self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"]) - - # BUFFER INVERTERS FOR W_EN - self.pre_w_en_bar_inst = self.add_inst(name="inv_pre_w_en_bar", - mod=self.inv2) - self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"]) - - self.w_en_inst = self.add_inst(name="inv_w_en2", - mod=self.inv8) - self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"]) - - - def place_we_row(self,row): - x_off = self.ctrl_dff_inst.width + self.internal_bus_width - (y_off,mirror)=self.get_offset(row) - - w_en_bar_offset = vector(x_off, y_off) - self.w_en_bar_inst.place(offset=w_en_bar_offset, - mirror=mirror) - if self.port_type == "rw": - x_off += self.nand3.width - else: - x_off += self.nand2.width - - pre_w_en_offset = vector(x_off, y_off) - self.pre_w_en_inst.place(offset=pre_w_en_offset, - mirror=mirror) - x_off += self.inv1.width - - pre_w_en_bar_offset = vector(x_off, y_off) - self.pre_w_en_bar_inst.place(offset=pre_w_en_bar_offset, - mirror=mirror) - x_off += self.inv2.width - - w_en_offset = vector(x_off, y_off) - self.w_en_inst.place(offset=w_en_offset, - mirror=mirror) - x_off += self.inv8.width - - self.row_end_inst.append(self.w_en_inst) - - - def route_rbl_in(self): - """ Connect the logic for the rbl_in generation """ - rbl_in_map = zip(["A", "B"], ["clk_buf_bar", "cs"]) - self.connect_vertical_bus(rbl_in_map, self.rbl_in_bar_inst, self.rail_offsets) - - # Connect the NAND3 output to the inverter - # The pins are assumed to extend all the way to the cell edge - rbl_in_bar_pos = self.rbl_in_bar_inst.get_pin("Z").center() - inv_in_pos = self.rbl_in_inst.get_pin("A").center() - mid1 = vector(inv_in_pos.x,rbl_in_bar_pos.y) - self.add_path("metal1",[rbl_in_bar_pos,mid1,inv_in_pos]) - - # Connect the output to the RBL - rbl_out_pos = self.rbl_in_inst.get_pin("Z").center() - rbl_in_pos = self.rbl_inst.get_pin("en").center() - mid1 = vector(rbl_in_pos.x,rbl_out_pos.y) - self.add_wire(("metal3","via2","metal2"),[rbl_out_pos,mid1,rbl_in_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rbl_out_pos, - rotate=90) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=rbl_out_pos, - rotate=90) - - def connect_rail_from_right(self,inst, pin, rail): - """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ - in_pos = inst.get_pin(pin).center() - rail_pos = vector(self.rail_offsets[rail].x, in_pos.y) - self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail_pos, - rotate=90) - - def connect_rail_from_right_m2m3(self,inst, pin, rail): - """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ - in_pos = inst.get_pin(pin).center() - rail_pos = vector(self.rail_offsets[rail].x, in_pos.y) - self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos]) - # Bring it up to M2 for M2/M3 routing - self.add_via_center(layers=("metal1","via1","metal2"), - offset=in_pos, - rotate=90) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=in_pos, - rotate=90) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=rail_pos, - rotate=90) - - - def connect_rail_from_left(self,inst, pin, rail): - """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ - in_pos = inst.get_pin(pin).lc() - rail_pos = vector(self.rail_offsets[rail].x, in_pos.y) - self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail_pos, - rotate=90) - - def connect_rail_from_left_m2m3(self,inst, pin, rail): - """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ - in_pos = inst.get_pin(pin).lc() - rail_pos = vector(self.rail_offsets[rail].x, in_pos.y) - self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos]) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=in_pos, - rotate=90) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=rail_pos, - rotate=90) - - - def route_wen(self): - if self.port_type == "rw": - wen_map = zip(["A", "B", "C"], ["clk_buf_bar", "cs", "we"]) - else: - wen_map = zip(["A", "B"], ["clk_buf_bar", "cs"]) - self.connect_vertical_bus(wen_map, self.w_en_bar_inst, self.rail_offsets) - - # Connect the NAND3 output to the inverter - # The pins are assumed to extend all the way to the cell edge - w_en_bar_pos = self.w_en_bar_inst.get_pin("Z").center() - inv_in_pos = self.pre_w_en_inst.get_pin("A").center() - mid1 = vector(inv_in_pos.x,w_en_bar_pos.y) - self.add_path("metal1",[w_en_bar_pos,mid1,inv_in_pos]) - - self.add_path("metal1",[self.pre_w_en_inst.get_pin("Z").center(), self.pre_w_en_bar_inst.get_pin("A").center()]) - self.add_path("metal1",[self.pre_w_en_bar_inst.get_pin("Z").center(), self.w_en_inst.get_pin("A").center()]) - - self.connect_output(self.w_en_inst, "Z", "w_en") - - def route_sen(self): - rbl_out_pos = self.rbl_inst.get_pin("out").bc() - in_pos = self.pre_s_en_bar_inst.get_pin("A").lc() - mid1 = vector(rbl_out_pos.x,in_pos.y) - self.add_wire(("metal1","via1","metal2"),[rbl_out_pos,mid1,in_pos]) - #s_en_pos = self.s_en.get_pin("Z").lc() - - self.add_path("metal1",[self.pre_s_en_bar_inst.get_pin("Z").center(), self.s_en_inst.get_pin("A").center()]) - - self.connect_output(self.s_en_inst, "Z", "s_en") - - def route_clk(self): - """ Route the clk and clk_buf_bar signal internally """ - - clk_pin = self.clkbuf_inst.get_pin("A") - self.add_layout_pin_segment_center(text="clk", - layer="metal2", - start=clk_pin.bc(), - end=clk_pin.bc().scale(1,0)) - - clkbuf_map = zip(["Z", "Zb"], ["clk_buf", "clk_buf_bar"]) - self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2")) - - # self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Z", "clk_buf") - # self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Zb", "clk_buf_bar") - self.connect_output(self.clkbuf_inst, "Z", "clk_buf") - self.connect_output(self.clkbuf_inst, "Zb", "clk_buf_bar") - def connect_output(self, inst, pin_name, out_name): """ Create an output pin on the right side from the pin of a given instance. """ diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 97e82e24..52c79473 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -59,7 +59,7 @@ class dff_array(design.design): self.dff_insts={} for row in range(self.rows): for col in range(self.columns): - name = "Xdff_r{0}_c{1}".format(row,col) + name = "dff_r{0}_c{1}".format(row,col) self.dff_insts[row,col]=self.add_inst(name=name, mod=self.dff) self.connect_inst([self.get_din_name(row,col), @@ -71,7 +71,7 @@ class dff_array(design.design): def place_dff_array(self): for row in range(self.rows): for col in range(self.columns): - name = "Xdff_r{0}_c{1}".format(row,col) + name = "dff_r{0}_c{1}".format(row,col) if (row % 2 == 0): base = vector(col*self.dff.width,row*self.dff.height) mirror = "R0" diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 48d0dc32..46adca7c 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -102,8 +102,7 @@ class dff_buf(design.design): mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx()) mid1 = vector(mid_x_offset, q_pin.cy()) mid2 = vector(mid_x_offset, a1_pin.cy()) - self.add_path("metal3", - [q_pin.center(), mid1, mid2, a1_pin.center()]) + self.add_path("metal3", [q_pin.center(), mid1, mid2, a1_pin.center()]) self.add_via_center(layers=("metal2","via2","metal3"), offset=q_pin.center()) self.add_via_center(layers=("metal2","via2","metal3"), @@ -114,8 +113,10 @@ class dff_buf(design.design): # Route inv1 z to inv2 a z1_pin = self.inv1_inst.get_pin("Z") a2_pin = self.inv2_inst.get_pin("A") - mid_point = vector(z1_pin.cx(), a2_pin.cy()) - self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()]) + mid_x_offset = 0.5*(z1_pin.cx() + a2_pin.cx()) + self.mid_qb_pos = vector(mid_x_offset, z1_pin.cy()) + mid2 = vector(mid_x_offset, a2_pin.cy()) + self.add_path("metal1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()]) def add_layout_pins(self): @@ -150,18 +151,22 @@ class dff_buf(design.design): height=din_pin.height()) dout_pin = self.inv2_inst.get_pin("Z") + mid_pos = dout_pin.center() + vector(self.m1_pitch,0) + q_pos = mid_pos - vector(0,self.m2_pitch) self.add_layout_pin_rect_center(text="Q", layer="metal2", - offset=dout_pin.center()) + offset=q_pos) + self.add_path("metal1", [dout_pin.center(), mid_pos, q_pos]) self.add_via_center(layers=("metal1","via1","metal2"), - offset=dout_pin.center()) + offset=q_pos) - dout_pin = self.inv2_inst.get_pin("A") + qb_pos = self.mid_qb_pos + vector(0,self.m2_pitch) self.add_layout_pin_rect_center(text="Qb", layer="metal2", - offset=dout_pin.center()) + offset=qb_pos) + self.add_path("metal1", [self.mid_qb_pos, qb_pos]) self.add_via_center(layers=("metal1","via1","metal2"), - offset=dout_pin.center()) + offset=qb_pos) diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index cf2bbef9..d5ad75ec 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -61,7 +61,7 @@ class dff_buf_array(design.design): self.dff_insts={} for row in range(self.rows): for col in range(self.columns): - name = "Xdff_r{0}_c{1}".format(row,col) + name = "dff_r{0}_c{1}".format(row,col) self.dff_insts[row,col]=self.add_inst(name=name, mod=self.dff) self.connect_inst([self.get_din_name(row,col), diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py deleted file mode 100644 index 4143f3e3..00000000 --- a/compiler/modules/dff_inv_array.py +++ /dev/null @@ -1,186 +0,0 @@ -import debug -import design -from tech import drc -from math import log -from vector import vector -from globals import OPTS -import dff_inv - -class dff_inv_array(design.design): - """ - This is a simple row (or multiple rows) of flops. - Unlike the data flops, these are never spaced out. - """ - unique_id = 1 - - def __init__(self, rows, columns, inv_size=2, name=""): - self.rows = rows - self.columns = columns - - if name=="": - name = "dff_inv_array_{0}x{1}_{2}".format(rows, columns, dff_inv_array.unique_id) - dff_inv_array.unique_id += 1 - design.design.__init__(self, name) - debug.info(1, "Creating {}".format(self.name)) - self.inv_size = inv_size - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - def create_netlist(self): - self.add_pins() - self.add_modules() - self.create_dff_array() - - def create_layout(self): - self.width = self.columns * self.dff.width - self.height = self.rows * self.dff.height - - self.place_dff_array() - self.add_layout_pins() - self.DRC_LVS() - - def add_modules(self): - self.dff = dff_inv.dff_inv(self.inv_size) - self.add_mod(self.dff) - - def add_pins(self): - for row in range(self.rows): - for col in range(self.columns): - self.add_pin(self.get_din_name(row,col)) - for row in range(self.rows): - for col in range(self.columns): - self.add_pin(self.get_dout_name(row,col)) - self.add_pin(self.get_dout_bar_name(row,col)) - self.add_pin("clk") - self.add_pin("vdd") - self.add_pin("gnd") - - def create_dff_array(self): - self.dff_insts={} - for row in range(self.rows): - for col in range(self.columns): - name = "Xdff_r{0}_c{1}".format(row,col) - self.dff_insts[row,col]=self.add_inst(name=name, - mod=self.dff) - self.connect_inst([self.get_din_name(row,col), - self.get_dout_name(row,col), - self.get_dout_bar_name(row,col), - "clk", - "vdd", - "gnd"]) - - def place_dff_array(self): - for row in range(self.rows): - for col in range(self.columns): - name = "Xdff_r{0}_c{1}".format(row,col) - if (row % 2 == 0): - base = vector(col*self.dff.width,row*self.dff.height) - mirror = "R0" - else: - base = vector(col*self.dff.width,(row+1)*self.dff.height) - mirror = "MX" - self.dff_insts[row,col].place(offset=base, - mirror=mirror) - - def get_din_name(self, row, col): - if self.columns == 1: - din_name = "din_{0}".format(row) - elif self.rows == 1: - din_name = "din_{0}".format(col) - else: - din_name = "din_{0}_{1}".format(row,col) - - return din_name - - def get_dout_name(self, row, col): - if self.columns == 1: - dout_name = "dout_{0}".format(row) - elif self.rows == 1: - dout_name = "dout_{0}".format(col) - else: - dout_name = "dout_{0}_{1}".format(row,col) - - return dout_name - - def get_dout_bar_name(self, row, col): - if self.columns == 1: - dout_bar_name = "dout_bar_{0}".format(row) - elif self.rows == 1: - dout_bar_name = "dout_bar_{0}".format(col) - else: - dout_bar_name = "dout_bar_{0}_{1}".format(row,col) - - return dout_bar_name - - def add_layout_pins(self): - for row in range(self.rows): - for col in range(self.columns): - # Adds power pin on left of row - vdd_pin=self.dff_insts[row,col].get_pin("vdd") - self.add_power_pin("vdd", vdd_pin.lc()) - - # Adds gnd pin on left of row - gnd_pin=self.dff_insts[row,col].get_pin("gnd") - self.add_power_pin("gnd", gnd_pin.lc()) - - - for row in range(self.rows): - for col in range(self.columns): - din_pin = self.dff_insts[row,col].get_pin("D") - debug.check(din_pin.layer=="metal2","DFF D pin not on metal2") - self.add_layout_pin(text=self.get_din_name(row,col), - layer=din_pin.layer, - offset=din_pin.ll(), - width=din_pin.width(), - height=din_pin.height()) - - dout_pin = self.dff_insts[row,col].get_pin("Q") - debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2") - self.add_layout_pin(text=self.get_dout_name(row,col), - layer=dout_pin.layer, - offset=dout_pin.ll(), - width=dout_pin.width(), - height=dout_pin.height()) - - dout_bar_pin = self.dff_insts[row,col].get_pin("Qb") - debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2") - self.add_layout_pin(text=self.get_dout_bar_name(row,col), - layer=dout_bar_pin.layer, - offset=dout_bar_pin.ll(), - width=dout_bar_pin.width(), - height=dout_bar_pin.height()) - - - # Create vertical spines to a single horizontal rail - clk_pin = self.dff_insts[0,0].get_pin("clk") - clk_ypos = 2*self.m3_pitch+self.m3_width - debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2") - if self.columns==1: - self.add_layout_pin(text="clk", - layer="metal2", - offset=clk_pin.ll().scale(1,0), - width=self.m2_width, - height=self.height) - else: - self.add_layout_pin_segment_center(text="clk", - layer="metal3", - start=vector(0,clk_ypos), - end=vector(self.width,clk_ypos)) - for col in range(self.columns): - clk_pin = self.dff_insts[0,col].get_pin("clk") - # Make a vertical strip for each column - self.add_rect(layer="metal2", - offset=clk_pin.ll().scale(1,0), - width=self.m2_width, - height=self.height) - # Drop a via to the M3 pin - self.add_via_center(layers=("metal2","via2","metal3"), - offset=vector(clk_pin.cx(),clk_ypos)) - - - - - def analytical_delay(self, slew, load=0.0): - return self.dff.analytical_delay(slew=slew, load=load) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 967b93cd..32ed6d7c 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -17,15 +17,11 @@ class hierarchical_decoder(design.design): """ Dynamically generated hierarchical decoder. """ - - def __init__(self, rows): - design.design.__init__(self, "hierarchical_decoder_{0}rows".format(rows)) - - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - b = self.mod_bitcell() - self.bitcell_height = b.height + unique_id = 1 + + def __init__(self, rows, height=None): + design.design.__init__(self, "hierarchical_decoder_{0}rows_{1}".format(rows,hierarchical_decoder.unique_id)) + hierarchical_decoder.unique_id += 1 self.NAND_FORMAT = "DEC_NAND_{0}" self.INV_FORMAT = "DEC_INV_{0}" @@ -33,6 +29,7 @@ class hierarchical_decoder(design.design): self.pre2x4_inst = [] self.pre3x8_inst = [] + self.cell_height = height self.rows = rows self.num_inputs = int(math.log(self.rows, 2)) (self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) @@ -60,21 +57,21 @@ class hierarchical_decoder(design.design): self.DRC_LVS() def add_modules(self): - self.inv = pinv() + self.inv = pinv(height=self.cell_height) self.add_mod(self.inv) - self.nand2 = pnand2() + self.nand2 = pnand2(height=self.cell_height) self.add_mod(self.nand2) - self.nand3 = pnand3() + self.nand3 = pnand3(height=self.cell_height) self.add_mod(self.nand3) self.add_decoders() def add_decoders(self): """ Create the decoders based on the number of pre-decodes """ - self.pre2_4 = pre2x4() + self.pre2_4 = pre2x4(height=self.cell_height) self.add_mod(self.pre2_4) - self.pre3_8 = pre3x8() + self.pre3_8 = pre3x8(height=self.cell_height) self.add_mod(self.pre3_8) def determine_predecodes(self,num_inputs): @@ -336,7 +333,7 @@ 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[1])*i + j + row = len(self.predec_groups[0])*j + i name = self.NAND_FORMAT.format(row) self.nand_inst.append(self.add_inst(name=name, mod=self.nand2)) @@ -352,8 +349,8 @@ 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[1])*len(self.predec_groups[2]) * i \ - + len(self.predec_groups[2])*j + k + row = (len(self.predec_groups[0])*len(self.predec_groups[1])) * k \ + + len(self.predec_groups[0])*j + i name = self.NAND_FORMAT.format(row) self.nand_inst.append(self.add_inst(name=name, @@ -523,8 +520,8 @@ class hierarchical_decoder(design.design): """ row_index = 0 if (self.num_inputs == 4 or self.num_inputs == 5): - for index_A in self.predec_groups[0]: - for index_B in self.predec_groups[1]: + for index_B in self.predec_groups[1]: + for index_A in self.predec_groups[0]: # FIXME: convert to connect_bus? predecode_name = "predecode_{}".format(index_A) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) @@ -533,9 +530,9 @@ class hierarchical_decoder(design.design): row_index = row_index + 1 elif (self.num_inputs > 5): - for index_A in self.predec_groups[0]: + for index_C in self.predec_groups[2]: for index_B in self.predec_groups[1]: - for index_C in self.predec_groups[2]: + for index_A in self.predec_groups[0]: # FIXME: convert to connect_bus? predecode_name = "predecode_{}".format(index_A) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 85ead465..7dfd443d 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -9,19 +9,18 @@ from globals import OPTS from pnand2 import pnand2 from pnand3 import pnand3 - class hierarchical_predecode(design.design): """ Pre 2x4 and 3x8 decoder shared code. """ - def __init__(self, input_number): + unique_id = 1 + + def __init__(self, input_number, height=None): self.number_of_inputs = input_number + self.cell_height = height self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) - design.design.__init__(self, name="pre{0}x{1}".format(self.number_of_inputs,self.number_of_outputs)) - - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) + design.design.__init__(self, name="pre{0}x{1}_{2}".format(self.number_of_inputs,self.number_of_outputs,hierarchical_predecode.unique_id)) + hierarchical_predecode.unique_id += 1 def add_pins(self): for k in range(self.number_of_inputs): @@ -34,7 +33,7 @@ class hierarchical_predecode(design.design): def add_modules(self): """ Add the INV and NAND gate modules """ - self.inv = pinv() + self.inv = pinv(height=self.cell_height) self.add_mod(self.inv) self.add_nand(self.number_of_inputs) @@ -43,9 +42,9 @@ class hierarchical_predecode(design.design): def add_nand(self,inputs): """ Create the NAND for the predecode input stage """ if inputs==2: - self.nand = pnand2() + self.nand = pnand2(height=self.cell_height) elif inputs==3: - self.nand = pnand3() + self.nand = pnand3(height=self.cell_height) else: debug.error("Invalid number of predecode inputs: {}".format(inputs),-1) @@ -90,7 +89,7 @@ class hierarchical_predecode(design.design): """ Create the input inverters to invert input signals for the decode stage. """ self.in_inst = [] for inv_num in range(self.number_of_inputs): - name = "Xpre_inv_{0}".format(inv_num) + name = "pre_inv_{0}".format(inv_num) self.in_inst.append(self.add_inst(name=name, mod=self.inv)) self.connect_inst(["in_{0}".format(inv_num), @@ -114,7 +113,7 @@ class hierarchical_predecode(design.design): """ Create inverters for the inverted output decode signals. """ self.inv_inst = [] for inv_num in range(self.number_of_outputs): - name = "Xpre_nand_inv_{}".format(inv_num) + name = "pre_nand_inv_{}".format(inv_num) self.inv_inst.append(self.add_inst(name=name, mod=self.inv)) self.connect_inst(["Z_{}".format(inv_num), diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index 4a7609bd..918172ea 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -9,8 +9,8 @@ class hierarchical_predecode2x4(hierarchical_predecode): """ Pre 2x4 decoder used in hierarchical_decoder. """ - def __init__(self): - hierarchical_predecode.__init__(self, 2) + def __init__(self, height=None): + hierarchical_predecode.__init__(self, 2, height) self.create_netlist() if not OPTS.netlist_only: diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index f0bb4b39..88bbbcd7 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -9,8 +9,8 @@ class hierarchical_predecode3x8(hierarchical_predecode): """ Pre 3x8 decoder used in hierarchical_decoder. """ - def __init__(self): - hierarchical_predecode.__init__(self, 3) + def __init__(self, height=None): + hierarchical_predecode.__init__(self, 3, height) self.create_netlist() if not OPTS.netlist_only: diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 7e0ee718..187c5fc5 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -33,7 +33,7 @@ class precharge_array(design.design): for i in range(self.columns): self.add_pin("bl_{0}".format(i)) self.add_pin("br_{0}".format(i)) - self.add_pin("en") + self.add_pin("en_bar") self.add_pin("vdd") def create_netlist(self): @@ -59,9 +59,9 @@ class precharge_array(design.design): def add_layout_pins(self): - self.add_layout_pin(text="en", + self.add_layout_pin(text="en_bar", layer="metal1", - offset=self.pc_cell.get_pin("en").ll(), + offset=self.pc_cell.get_pin("en_bar").ll(), width=self.width, height=drc("minwidth_metal1")) @@ -94,7 +94,7 @@ class precharge_array(design.design): mod=self.pc_cell, offset=offset) self.local_insts.append(inst) - self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en", "vdd"]) + self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en_bar", "vdd"]) def place_insts(self): diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index e349fa89..dcbc53d4 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -265,15 +265,8 @@ class replica_bitline(design.design): pin = self.rbl_inv_inst.get_pin("vdd") self.add_power_pin("vdd", pin.lc()) - # Replica bitcell needs to be routed up to M3 pin=self.rbc_inst.get_pin("vdd") - # Don't rotate this via to vit in FreePDK45. In the custom cell, the pin cannot be placed - # directly on vdd or there will be a drc error with a wordline. Place the pin slightly farther - # away then route to it. A better solution would be to rotate the m1 in the via or move the pin - # a m1_pitch below the entire cell. - pin_extension = pin.center() - vector(0,self.m1_pitch) - self.add_power_pin("vdd", pin_extension, rotate=0) - self.add_path("metal1", [pin.center(), pin_extension]) + self.add_power_pin("vdd", pin.center(), 0, pin.layer) for pin in self.rbc_inst.get_pins("gnd"): self.add_power_pin("gnd", pin.center()) diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index ed379bcc..1b56a29a 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -44,7 +44,7 @@ class wordline_driver(design.design): # Outputs from wordline_driver. for i in range(self.rows): self.add_pin("wl_{0}".format(i)) - self.add_pin("en") + self.add_pin("en_bar") self.add_pin("vdd") self.add_pin("gnd") @@ -67,7 +67,7 @@ class wordline_driver(design.design): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ # Find the x offsets for where the vias/pins should be placed - a_xoffset = self.inv1_inst[0].rx() + a_xoffset = self.nand_inst[0].rx() b_xoffset = self.inv2_inst[0].lx() for num in range(self.rows): # this will result in duplicate polygons for rails, but who cares @@ -95,24 +95,16 @@ class wordline_driver(design.design): def create_drivers(self): - self.inv1_inst = [] self.nand_inst = [] self.inv2_inst = [] for row in range(self.rows): - name_inv1 = "wl_driver_inv_en{}".format(row) name_nand = "wl_driver_nand{}".format(row) name_inv2 = "wl_driver_inv{}".format(row) - # add inv1 based on the info above - self.inv1_inst.append(self.add_inst(name=name_inv1, - mod=self.inv_no_output)) - self.connect_inst(["en", - "en_bar_{0}".format(row), - "vdd", "gnd"]) # add nand 2 self.nand_inst.append(self.add_inst(name=name_nand, mod=self.nand2)) - self.connect_inst(["en_bar_{0}".format(row), + self.connect_inst(["en_bar", "in_{0}".format(row), "wl_bar_{0}".format(row), "vdd", "gnd"]) @@ -125,8 +117,7 @@ class wordline_driver(design.design): def place_drivers(self): - inv1_xoffset = 2*self.m1_width + 5*self.m1_space - nand2_xoffset = inv1_xoffset + self.inv.width + nand2_xoffset = 2*self.m1_width + 5*self.m1_space inv2_xoffset = nand2_xoffset + self.nand2.width self.width = inv2_xoffset + self.inv.width @@ -140,13 +131,9 @@ class wordline_driver(design.design): y_offset = self.inv.height*row inst_mirror = "R0" - inv1_offset = [inv1_xoffset, y_offset] nand2_offset=[nand2_xoffset, y_offset] inv2_offset=[inv2_xoffset, y_offset] - # add inv1 based on the info above - self.inv1_inst[row].place(offset=inv1_offset, - mirror=inst_mirror) # add nand 2 self.nand_inst[row].place(offset=nand2_offset, mirror=inst_mirror) @@ -159,7 +146,7 @@ class wordline_driver(design.design): """ Route all of the signals """ # Wordline enable connection - en_pin=self.add_layout_pin(text="en", + en_pin=self.add_layout_pin(text="en_bar", layer="metal2", offset=[self.m1_width + 2*self.m1_space,0], width=self.m2_width, @@ -167,12 +154,11 @@ class wordline_driver(design.design): for row in range(self.rows): - inv1_inst = self.inv1_inst[row] nand_inst = self.nand_inst[row] inv2_inst = self.inv2_inst[row] - # en connection - a_pin = inv1_inst.get_pin("A") + # en_bar connection + a_pin = nand_inst.get_pin("A") a_pos = a_pin.lc() clk_offset = vector(en_pin.bc().x,a_pos.y) self.add_segment_center(layer="metal1", @@ -181,13 +167,6 @@ class wordline_driver(design.design): self.add_via_center(layers=("metal1", "via1", "metal2"), offset=clk_offset) - # first inv to nand2 A - zb_pos = inv1_inst.get_pin("Z").bc() - zu_pos = inv1_inst.get_pin("Z").uc() - bl_pos = nand_inst.get_pin("A").lc() - br_pos = nand_inst.get_pin("A").rc() - self.add_path("metal1", [zb_pos, zu_pos, bl_pos, br_pos]) - # Nand2 out to 2nd inv zr_pos = nand_inst.get_pin("Z").rc() al_pos = inv2_inst.get_pin("A").lc() diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 61fe8c24..3b5e75d9 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -68,7 +68,7 @@ class write_driver_array(design.design): def create_write_array(self): self.driver_insts = {} for i in range(0,self.columns,self.words_per_row): - name = "Xwrite_driver{}".format(i) + name = "write_driver{}".format(i) index = int(i/self.words_per_row) self.driver_insts[index]=self.add_inst(name=name, mod=self.driver) diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py new file mode 100644 index 00000000..00b6731c --- /dev/null +++ b/compiler/pgates/pand2.py @@ -0,0 +1,128 @@ +import debug +from tech import drc +from math import log +from vector import vector +from globals import OPTS +from pnand2 import pnand2 +from pinv import pinv +import pgate + +class pand2(pgate.pgate): + """ + This is a simple buffer used for driving loads. + """ + from importlib import reload + c = reload(__import__(OPTS.bitcell)) + bitcell = getattr(c, OPTS.bitcell) + + unique_id = 1 + + def __init__(self, size=1, height=None, name=""): + + self.size = size + + if name=="": + name = "pand2_{0}_{1}".format(size, pand2.unique_id) + pand2.unique_id += 1 + + pgate.pgate.__init__(self, name, height) + debug.info(1, "Creating {}".format(self.name)) + + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + # Shield the cap, but have at least a stage effort of 4 + self.nand = pnand2(height=self.height) + self.add_mod(self.nand) + + self.inv = pinv(size=self.size, height=self.height) + self.add_mod(self.inv) + + def create_layout(self): + self.width = self.nand.width + self.inv.width + self.place_insts() + self.add_wires() + self.add_layout_pins() + + def add_pins(self): + self.add_pin("A") + self.add_pin("B") + self.add_pin("Z") + self.add_pin("vdd") + self.add_pin("gnd") + + def create_insts(self): + self.nand_inst=self.add_inst(name="pand2_nand", + mod=self.nand) + self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) + + self.inv_inst=self.add_inst(name="pand2_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0,0)) + + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(),0)) + + def add_wires(self): + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.inv_inst.get_pin("A") + mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) + mid2_point = vector(mid1_point, a2_pin.cy()) + self.add_path("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) + + + def add_layout_pins(self): + # Continous vdd rail along with label. + vdd_pin=self.inv_inst.get_pin("vdd") + self.add_layout_pin(text="vdd", + layer="metal1", + offset=vdd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + # Continous gnd rail along with label. + gnd_pin=self.inv_inst.get_pin("gnd") + self.add_layout_pin(text="gnd", + layer="metal1", + offset=gnd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + pin = self.inv_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A","B"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + + + def analytical_delay(self, slew, load=0.0): + """ Calculate the analytical delay of DFF-> INV -> INV """ + nand_delay = selfnand.analytical_delay(slew=slew, load=self.inv.input_load()) + inv_delay = self.inv.analytical_delay(slew=nand_delay.slew, load=load) + return nand_delay + inv_delay + + diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index 0d30a89b..d0c112fc 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -1,12 +1,12 @@ import debug -import design from tech import drc from math import log from vector import vector from globals import OPTS from pinv import pinv +import pgate -class pbuf(design.design): +class pbuf(pgate.pgate): """ This is a simple buffer used for driving loads. """ @@ -16,39 +16,32 @@ class pbuf(design.design): unique_id = 1 - def __init__(self, driver_size=4, height=bitcell.height, name=""): + def __init__(self, size=4, height=None, name=""): - stage_effort = 4 - # FIXME: Change the number of stages to support high drives. + self.stage_effort = 4 + self.size = size + self.height = height if name=="": - name = "pbuf_{0}_{1}".format(driver_size, pbuf.unique_id) + name = "pbuf_{0}_{1}".format(self.size, pbuf.unique_id) pbuf.unique_id += 1 - design.design.__init__(self, name) - debug.info(1, "Creating {}".format(self.name)) + pgate.pgate.__init__(self, name, height) + debug.info(1, "creating {0} with size of {1}".format(self.name,self.size)) - - # Shield the cap, but have at least a stage effort of 4 - input_size = max(1,int(driver_size/stage_effort)) - self.inv1 = pinv(size=input_size, height=height) # 1 - self.add_mod(self.inv1) - - self.inv2 = pinv(size=driver_size, height=height) # 2 - self.add_mod(self.inv2) + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() - self.width = self.inv1.width + self.inv2.width - self.height = self.inv1.height - self.create_layout() - - #self.offset_all_coordinates() - - self.DRC_LVS() + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() def create_layout(self): - self.add_pins() - self.add_insts() + self.width = self.inv1.width + self.inv2.width + self.place_insts() self.add_wires() self.add_layout_pins() @@ -58,19 +51,31 @@ class pbuf(design.design): self.add_pin("vdd") self.add_pin("gnd") - def add_insts(self): - # Add INV1 to the right + def create_modules(self): + # Shield the cap, but have at least a stage effort of 4 + input_size = max(1,int(self.size/self.stage_effort)) + self.inv1 = pinv(size=input_size, height=self.height) + self.add_mod(self.inv1) + + self.inv2 = pinv(size=self.size, height=self.height) + self.add_mod(self.inv2) + + def create_insts(self): self.inv1_inst=self.add_inst(name="buf_inv1", - mod=self.inv1, - offset=vector(0,0)) + mod=self.inv1) self.connect_inst(["A", "zb_int", "vdd", "gnd"]) - # Add INV2 to the right self.inv2_inst=self.add_inst(name="buf_inv2", - mod=self.inv2, - offset=vector(self.inv1_inst.rx(),0)) + mod=self.inv2) self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add INV1 to the right + self.inv1_inst.place(vector(0,0)) + + # Add INV2 to the right + self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) def add_wires(self): @@ -100,17 +105,17 @@ class pbuf(design.design): z_pin = self.inv2_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Z", - layer="metal2", - offset=z_pin.center()) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=z_pin.center()) + layer=z_pin.layer, + offset=z_pin.center(), + width=z_pin.width(), + height=z_pin.height()) a_pin = self.inv1_inst.get_pin("A") self.add_layout_pin_rect_center(text="A", - layer="metal2", - offset=a_pin.center()) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=a_pin.center()) + layer=a_pin.layer, + offset=a_pin.center(), + width=a_pin.width(), + height=a_pin.height()) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 1a31e3be..440c31d0 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -33,7 +33,6 @@ class pnand2(pgate.pgate): self.create_netlist() if not OPTS.netlist_only: self.create_layout() - #self.DRC_LVS() def create_netlist(self): @@ -192,26 +191,33 @@ class pnand2(pgate.pgate): """ Route the Z output """ # PMOS1 drain pmos_pin = self.pmos1_inst.get_pin("D") + top_pin_offset = pmos_pin.center() # NMOS2 drain - nmos_pin = self.nmos2_inst.get_pin("D") + nmos_pin = self.nmos2_inst.get_pin("D") + bottom_pin_offset = nmos_pin.center() + # Output pin - mid_offset = vector(nmos_pin.center().x,self.inputA_yoffset) + out_offset = vector(nmos_pin.center().x + 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) + mid2_offset = vector(out_offset.x, bottom_pin_offset.y) self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=pmos_pin.center()) self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=nmos_pin.center()) self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=mid_offset, + offset=out_offset, rotate=90) # PMOS1 to mid-drain to NMOS2 drain - self.add_path("metal2",[pmos_pin.bc(), mid_offset, nmos_pin.uc()]) + self.add_path("metal2",[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="metal1", - offset=mid_offset, + offset=out_offset, width=contact.m1m2.first_layer_height, height=contact.m1m2.first_layer_width) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 5f9c1e5b..191b9add 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -51,7 +51,7 @@ class precharge(pgate.pgate): self.DRC_LVS() def add_pins(self): - self.add_pin_list(["bl", "br", "en", "vdd"]) + self.add_pin_list(["bl", "br", "en_bar", "vdd"]) def add_ptx(self): """ @@ -92,15 +92,15 @@ class precharge(pgate.pgate): self.lower_pmos_inst=self.add_inst(name="lower_pmos", mod=self.pmos) - self.connect_inst(["bl", "en", "br", "vdd"]) + self.connect_inst(["bl", "en_bar", "br", "vdd"]) self.upper_pmos1_inst=self.add_inst(name="upper_pmos1", mod=self.pmos) - self.connect_inst(["bl", "en", "vdd", "vdd"]) + self.connect_inst(["bl", "en_bar", "vdd", "vdd"]) self.upper_pmos2_inst=self.add_inst(name="upper_pmos2", mod=self.pmos) - self.connect_inst(["br", "en", "vdd", "vdd"]) + self.connect_inst(["br", "en_bar", "vdd", "vdd"]) def place_ptx(self): @@ -161,7 +161,7 @@ class precharge(pgate.pgate): rotate=90) # adds the en rail on metal1 - self.add_layout_pin_segment_center(text="en", + self.add_layout_pin_segment_center(text="en_bar", layer="metal1", start=offset.scale(0,1), end=offset.scale(0,1)+vector(self.width,0)) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 07d04028..eb50e6df 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -68,12 +68,12 @@ class ptx(design.design): # 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 - 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"), - perimeter_sd, - area_sd) + self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4:.2f}u ps={4:.2f}u as={5:.2f}p ad={5:.2f}p".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly"), + perimeter_sd, + area_sd) self.spice.append("\n* ptx " + self.spice_device) # self.spice.append(".ENDS {0}".format(self.name)) diff --git a/compiler/sram.py b/compiler/sram.py index 0c977868..34074321 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -34,8 +34,6 @@ class sram(): from sram_1bank import sram_1bank as sram elif self.num_banks == 2: from sram_2bank import sram_2bank as sram - elif self.num_banks == 4: - from sram_4bank import sram_4bank as sram else: debug.error("Invalid number of banks.",-1) diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index 49ad4f9b..be7f20ff 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -61,15 +61,18 @@ class sram_1bank(sram_base): row_addr_pos = [None]*len(self.all_ports) col_addr_pos = [None]*len(self.all_ports) data_pos = [None]*len(self.all_ports) - - # This is M2 pitch even though it is on M1 to help stem via spacings on the trunk - data_gap = self.m2_pitch*(self.word_size+1) + # This is M2 pitch even though it is on M1 to help stem via spacings on the trunk + # The M1 pitch is for supply rail spacings + max_gap_size = self.m2_pitch*max(self.word_size+1,self.col_addr_size+1) + 2*self.m1_pitch + # Port 0 port = 0 - # This includes 2 M2 pitches for the row addr clock line + + # This includes 2 M2 pitches for the row addr clock line. + # It is also placed to align with the column decoder (if it exists hence the bank gap) control_pos[port] = vector(-self.control_logic_insts[port].width - 2*self.m2_pitch, - self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y) + self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - self.bank.m2_gap) self.control_logic_insts[port].place(control_pos[port]) # The row address bits are placed above the control logic aligned on the right. @@ -81,8 +84,8 @@ class sram_1bank(sram_base): # Add the col address flops below the bank to the left of the lower-left of bank array if self.col_addr_dff: - col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.central_bus_width, - -data_gap - self.col_addr_dff_insts[port].height) + col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, + -max_gap_size - self.col_addr_dff_insts[port].height) self.col_addr_dff_insts[port].place(col_addr_pos[port]) # Add the data flops below the bank to the right of the lower-left of bank array @@ -92,16 +95,18 @@ class sram_1bank(sram_base): # sense amps. if port in self.write_ports: data_pos[port] = vector(self.bank.bank_array_ll.x, - -data_gap - self.data_dff_insts[port].height) + -max_gap_size - self.data_dff_insts[port].height) self.data_dff_insts[port].place(data_pos[port]) if len(self.all_ports)>1: # Port 1 port = 1 + # This includes 2 M2 pitches for the row addr clock line + # It is also placed to align with the column decoder (if it exists hence the bank gap) control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch, - self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y) + self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y + self.bank.m2_gap) self.control_logic_insts[port].place(control_pos[port], mirror="MY") # The row address bits are placed above the control logic aligned on the left. @@ -113,8 +118,8 @@ class sram_1bank(sram_base): # Add the col address flops above the bank to the right of the upper-right of bank array if self.col_addr_dff: - col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.central_bus_width, - self.bank_inst.uy() + data_gap + self.col_addr_dff_insts[port].height) + col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, + self.bank.height + max_gap_size + self.col_addr_dff_insts[port].height) self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX") # Add the data flops above the bank to the left of the upper-right of bank array @@ -124,7 +129,7 @@ class sram_1bank(sram_base): # sense amps. if port in self.write_ports: data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, - self.bank.uy() + data_gap + self.data_dff_insts[port].height) + self.bank.height + max_gap_size + self.data_dff_insts[port].height) self.data_dff_insts[port].place(data_pos[port], mirror="MX") @@ -178,27 +183,11 @@ class sram_1bank(sram_base): # Connect all of these clock pins to the clock in the central bus # This is something like a "spine" clock distribution. The two spines # are clk_buf and clk_buf_bar + control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf") + control_clk_buf_pos = control_clk_buf_pin.center() - bank_clk_buf_pin = self.bank_inst.get_pin("clk_buf{}".format(port)) - bank_clk_buf_pos = bank_clk_buf_pin.center() - bank_clk_buf_bar_pin = self.bank_inst.get_pin("clk_buf_bar{}".format(port)) - bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center() - - if self.col_addr_dff: - dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk") - dff_clk_pos = dff_clk_pin.center() - mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y) - self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos]) - - if port in self.write_ports: - data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") - data_dff_clk_pos = data_dff_clk_pin.center() - mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y) - self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos]) - # This uses a metal2 track to the right (for port0) of the control/row addr DFF # to route vertically. For port1, it is to the left. - control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf") row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk") if port%2: control_clk_buf_pos = control_clk_buf_pin.lc() @@ -210,17 +199,38 @@ class sram_1bank(sram_base): row_addr_clk_pos = row_addr_clk_pin.rc() mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch, row_addr_clk_pos.y) - mid2_pos = vector(mid1_pos.x, - control_clk_buf_pos.y) + + # This is the steiner point where the net branches out + clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y) + self.add_path("metal1", [control_clk_buf_pos, clk_steiner_pos]) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=clk_steiner_pos, + rotate=90) + # Note, the via to the control logic is taken care of when we route # the control logic to the bank - self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, mid2_pos, control_clk_buf_pos]) + self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos, control_clk_buf_pos]) + if self.col_addr_dff: + dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk") + dff_clk_pos = dff_clk_pin.center() + mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y) + self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, clk_steiner_pos]) + + if port in self.write_ports: + data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") + data_dff_clk_pos = data_dff_clk_pin.center() + mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y) + self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos]) + def route_control_logic(self): """ Route the outputs from the control logic module """ for port in self.all_ports: for signal in self.control_logic_outputs[port]: + # The clock gets routed separately and is not a part of the bank + if "clk" in signal: + continue src_pin = self.control_logic_insts[port].get_pin(signal) dest_pin = self.bank_inst.get_pin(signal+"{}".format(port)) self.connect_rail_from_left_m2m3(src_pin, dest_pin) @@ -268,17 +278,20 @@ class sram_1bank(sram_base): """ Connect the output of the data flops to the write driver """ # This is where the channel will start (y-dimension at least) for port in self.write_ports: - offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) + if port%2: + offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch) + else: + offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) + dff_names = ["dout_{}".format(x) for x in range(self.word_size)] + dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] + bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)] - - route_map = list(zip(bank_names, dff_names)) - dff_pins = {key: self.data_dff_insts[port].get_pin(key) for key in dff_names } - bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names } - # Combine the dff and bank pins into a single dictionary of pin name to pin. - all_pins = {**dff_pins, **bank_pins} - self.create_horizontal_channel_route(route_map, all_pins, offset) + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + + route_map = list(zip(bank_pins, dff_pins)) + self.create_horizontal_channel_route(route_map, offset) diff --git a/compiler/sram_4bank.py b/compiler/sram_4bank.py deleted file mode 100644 index c9309749..00000000 --- a/compiler/sram_4bank.py +++ /dev/null @@ -1,331 +0,0 @@ -import sys -from tech import drc, spice -import debug -from math import log,sqrt,ceil -import datetime -import getpass -from vector import vector -from globals import OPTS, print_time - -from sram_base import sram_base -from bank import bank -from dff_buf_array import dff_buf_array -from dff_array import dff_array - -class sram_4bank(sram_base): - """ - Procedures specific to a four bank SRAM. - """ - def __init__(self, name, sram_config): - sram_base.__init__(self, name, sram_config) - - def compute_bank_offsets(self): - """ Compute the overall offsets for a four bank SRAM """ - - # The main difference is that the four bank SRAM has the data bus in the middle of the four banks - # as opposed to the top of the banks. - - # In 4 bank SRAM, the height is determined by the bank decoder and address flop - self.vertical_bus_height = 2*self.bank.height + 4*self.bank_to_bus_distance + self.data_bus_height \ - + self.supply_bus_height + self.msb_decoder.height + self.msb_address.width - # The address bus extends down through the power rails, but control and bank_sel bus don't - self.addr_bus_height = self.vertical_bus_height - - self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 0) - self.data_bus_offset = vector(0, self.bank.height + self.bank_to_bus_distance) - self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height \ - + self.bank.height + 2*self.bank_to_bus_distance) - self.control_bus_offset = vector(0, self.supply_bus_offset.y + self.supply_bus_height) - self.bank_sel_bus_offset = self.vertical_bus_offset + vector(self.m2_pitch*self.control_size,0) - self.addr_bus_offset = self.bank_sel_bus_offset.scale(1,0) + vector(self.m2_pitch*self.num_banks,0) - - # Control is placed at the top above the control bus and everything - self.control_logic_position = vector(0, self.control_bus_offset.y + self.control_bus_height + self.m1_pitch) - - # Bank select flops get put to the right of control logic above bank1 and the buses - # Leave a pitch to get the vdd rails up to M2 - self.msb_address_position = vector(self.bank_inst[1].lx() + 3*self.supply_rail_pitch, - self.supply_bus_offset.y + self.supply_bus_height \ - + 2*self.m1_pitch + self.msb_address.width) - - # Decoder goes above the MSB address flops, and is flipped in Y - # separate the two by a bank to bus distance for nwell rules, just in case - self.msb_decoder_position = self.msb_address_position + vector(self.msb_decoder.width, self.bank_to_bus_distance) - - - def add_modules(self): - """ Adds the modules and the buses to the top level """ - - self.compute_bus_sizes() - - self.add_banks() - - self.compute_bank_offsets() - - self.add_busses() - - self.add_logic() - - self.width = self.bank_inst[1].ur().x - self.height = max(self.control_logic_inst.uy(),self.msb_decoder_inst.uy()) - - def add_banks(self): - - # Placement of bank 0 (upper left) - bank_position_0 = vector(self.bank.width, - self.bank.height + self.data_bus_height + 2*self.bank_to_bus_distance) - self.bank_inst=[self.add_bank(0, bank_position_0, 1, -1)] - - # Placement of bank 1 (upper right) - x_off = self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance - bank_position_1 = vector(x_off, bank_position_0.y) - self.bank_inst.append(self.add_bank(1, bank_position_1, 1, 1)) - - # Placement of bank 2 (bottom left) - y_off = self.bank.height - bank_position_2 = vector(bank_position_0.x, y_off) - self.bank_inst.append(self.add_bank(2, bank_position_2, -1, -1)) - - # Placement of bank 3 (bottom right) - bank_position_3 = vector(bank_position_1.x, bank_position_2.y) - self.bank_inst.append(self.add_bank(3, bank_position_3, -1, 1)) - - - - def add_logic(self): - """ Add the control and MSB decode/bank select logic for four banks """ - - - self.add_control_logic(position=self.control_logic_position) - - self.msb_address_inst = self.add_inst(name="msb_address", - mod=self.msb_address, - offset=self.msb_address_position, - rotate=270) - - self.msb_bank_sel_addr = ["ADDR[{}]".format(i) for i in range(self.addr_size-2,self.addr_size,1)] - temp = list(self.msb_bank_sel_addr) - temp.extend(["msb{0}[{1}]".format(j,i) for i in range(2) for j in ["","_bar"]]) - temp.extend(["clk_buf", "vdd", "gnd"]) - self.connect_inst(temp) - - self.msb_decoder_inst = self.add_inst(name="msb_decoder", - mod=self.msb_decoder, - offset=self.msb_decoder_position, - mirror="MY") - temp = ["msb[{}]".format(i) for i in range(2)] - temp.extend(["bank_sel[{}]".format(i) for i in range(4)]) - temp.extend(["vdd", "gnd"]) - self.connect_inst(temp) - - def route_double_msb_address(self): - """ Route two MSB address bits and the bank decoder for 4-bank SRAM """ - - # connect the MSB flops to the address input bus - for i in [0,1]: - msb_pins = self.msb_address_inst.get_pins("din_{}".format(i)) - for msb_pin in msb_pins: - if msb_pin.layer == "metal3": - msb_pin_pos = msb_pin.lc() - break - rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr[i]].x,msb_pin_pos.y) - self.add_path("metal3",[msb_pin_pos,rail_pos]) - self.add_via_center(("metal2","via2","metal3"),rail_pos) - - # Connect clk - clk_pin = self.msb_address_inst.get_pin("clk") - clk_pos = clk_pin.bc() - rail_pos = self.horz_control_bus_positions["clk_buf"] - bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y) - self.add_path("metal1",[clk_pos,bend_pos,rail_pos]) - - # Connect bank decoder outputs to the bank select vertical bus wires - for i in range(self.num_banks): - msb_pin = self.msb_decoder_inst.get_pin("out_{}".format(i)) - msb_pin_pos = msb_pin.lc() - rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,msb_pin_pos.y) - self.add_path("metal1",[msb_pin_pos,rail_pos]) - self.add_via_center(("metal1","via1","metal2"),rail_pos) - - # connect MSB flop outputs to the bank decoder inputs - msb_pin = self.msb_address_inst.get_pin("dout[0]") - msb_pin_pos = msb_pin.rc() - in_pin = self.msb_decoder_inst.get_pin("in[0]") - in_pos = in_pin.bc() + vector(0,1*self.m2_pitch,) # pin is up from bottom - out_pos = msb_pin_pos + vector(1*self.m2_pitch,0) # route out to the right - up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer - self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos]) - self.add_via_center(("metal1","via1","metal2"),in_pos) - self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90) - - msb_pin = self.msb_address_inst.get_pin("dout[1]") - msb_pin_pos = msb_pin.rc() - in_pin = self.msb_decoder_inst.get_pin("in[1]") - in_pos = in_pin.bc() + vector(0,self.bitcell.height+self.m2_pitch) # route the next row up - out_pos = msb_pin_pos + vector(2*self.m2_pitch,0) # route out to the right - up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer - self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos]) - self.add_via_center(("metal1","via1","metal2"),in_pos) - self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90) - - self.route_double_msb_address_supplies() - - def route_double_msb_address_supplies(self): - """ Route the vdd/gnd bits of the 2-bit bank decoder. """ - - # Route the right-most vdd/gnd of the right upper bank to the top of the decoder - vdd_pins = self.bank_inst[1].get_pins("vdd") - left_bank_vdd_pin = None - right_bank_vdd_pin = None - for vdd_pin in vdd_pins: - if vdd_pin.layer != "metal2": - continue - if left_bank_vdd_pin == None or vdd_pin.lx()right_bank_vdd_pin.lx(): - right_bank_vdd_pin = vdd_pin - # Route to top - self.add_rect(layer="metal2", - offset=vdd_pin.ul(), - height=self.height-vdd_pin.uy(), - width=vdd_pin.width()) - - gnd_pins = self.bank_inst[1].get_pins("gnd") - left_bank_gnd_pin = None - right_bank_gnd_pin = None - for gnd_pin in gnd_pins: - if gnd_pin.layer != "metal2": - continue - if left_bank_gnd_pin == None or gnd_pin.lx()right_bank_gnd_pin.lx(): - right_bank_gnd_pin = gnd_pin - # Route to top - self.add_rect(layer="metal2", - offset=gnd_pin.ul(), - height=self.height-gnd_pin.uy(), - width=gnd_pin.width()) - - # Connect bank decoder vdd/gnd supplies using the previous bank pins - vdd_pins = self.msb_decoder_inst.get_pins("vdd") - for vdd_pin in vdd_pins: - if vdd_pin.layer != "metal1": - continue - rail1_pos = vector(left_bank_vdd_pin.cx(),vdd_pin.cy()) - rail2_pos = vector(right_bank_vdd_pin.cx(),vdd_pin.cy()) - self.add_path("metal1",[rail1_pos,rail2_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail1_pos, - rotate=90, - size=[1,3]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail2_pos, - rotate=90, - size=[1,3]) - gnd_pins = self.msb_decoder_inst.get_pins("gnd") - for gnd_pin in gnd_pins: - if gnd_pin.layer != "metal1": - continue - rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy()) - rail2_pos = vector(right_bank_gnd_pin.cx(),gnd_pin.cy()) - self.add_path("metal1",[rail1_pos,rail2_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail1_pos, - rotate=90, - size=[1,3]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail2_pos, - rotate=90, - size=[1,3]) - - # connect the bank MSB flop supplies - vdd_pins = self.msb_address_inst.get_pins("vdd") - # vdd pins go down to the rail - for vdd_pin in vdd_pins: - if vdd_pin.layer != "metal1": - continue - vdd_pos = vdd_pin.bc() - down_pos = vdd_pos - vector(0,self.m1_pitch) - rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y) - self.add_path("metal1",[vdd_pos,down_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=down_pos, - rotate=90) - self.add_path("metal2",[down_pos,rail_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail_pos) - # gnd pins go right to the rail - gnd_pins = self.msb_address_inst.get_pins("gnd") - for gnd_pin in gnd_pins: - if gnd_pin.layer != "metal2": - continue - rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy()) - self.add_path("metal1",[rail1_pos,gnd_pin.lc()]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=gnd_pin.lc(), - rotate=90) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail1_pos, - rotate=90, - size=[1,3]) - - - def route(self): - """ Route all of the signals for the four bank SRAM. """ - - self.route_shared_banks() - - # connect the data output to the data bus - for n in self.data_bus_names: - for i in [0,1]: - pin_pos = self.bank_inst[i].get_pin(n).bc() - rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y) - self.add_path("metal2",[pin_pos,rail_pos]) - self.add_via_center(("metal2","via2","metal3"),rail_pos) - - for i in [2,3]: - pin_pos = self.bank_inst[i].get_pin(n).uc() - rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y) - self.add_path("metal2",[pin_pos,rail_pos]) - self.add_via_center(("metal2","via2","metal3"),rail_pos) - - # route msb address bits - # route 2:4 decoder - self.route_double_msb_address() - - # connect the banks to the vertical address bus - # connect the banks to the vertical control bus - for n in self.addr_bus_names + self.control_bus_names: - # Skip these from the horizontal bus - if n in ["vdd", "gnd"]: continue - # This will be the bank select, so skip it - if n in self.msb_bank_sel_addr: continue - - for bank_id in [0,2]: - pin0_pos = self.bank_inst[bank_id].get_pin(n).rc() - pin1_pos = self.bank_inst[bank_id+1].get_pin(n).lc() - rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y) - self.add_path("metal3",[pin0_pos,pin1_pos]) - self.add_via_center(("metal2","via2","metal3"),rail_pos) - - - self.route_bank_supply_rails(left_banks=[0,2], bottom_banks=[2,3]) - - - def add_lvs_correspondence_points(self): - """ - This adds some points for easier debugging if LVS goes wrong. - These should probably be turned off by default though, since extraction - will show these as ports in the extracted netlist. - """ - - if self.num_banks==1: return - - for n in self.control_bus_names: - self.add_label(text=n, - layer="metal2", - offset=self.vert_control_bus_positions[n]) - for n in self.bank_sel_bus_names: - self.add_label(text=n, - layer="metal2", - offset=self.vert_control_bus_positions[n]) diff --git a/compiler/sram_base.py b/compiler/sram_base.py index 29c3cbb9..3182c5b3 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -140,11 +140,16 @@ class sram_base(design): # The order of the control signals on the control bus: self.control_bus_names = [] for port in self.all_ports: - self.control_bus_names[port] = ["clk_buf{}".format(port), "clk_buf_bar{}".format(port)] - if (self.port_id[port] == "rw") or (self.port_id[port] == "w"): - self.control_bus_names[port].append("w_en{}".format(port)) - if (self.port_id[port] == "rw") or (self.port_id[port] == "r"): - self.control_bus_names[port].append("s_en{}".format(port)) + self.control_bus_names[port] = ["clk_buf{}".format(port)] + wen = "w_en{}".format(port) + sen = "s_en{}".format(port) + pen = "p_en_bar{}".format(port) + if self.port_id[port] == "r": + self.control_bus_names[port].extend([sen, pen]) + elif self.port_id[port] == "w": + self.control_bus_names[port].extend([wen]) + else: + self.control_bus_names[port].extend([sen, wen, pen]) self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2", pitch=self.m2_pitch, offset=self.vertical_bus_offset, @@ -287,11 +292,12 @@ class sram_base(design): temp.append("bank_sel{0}[{1}]".format(port,bank_num)) for port in self.read_ports: temp.append("s_en{0}".format(port)) + for port in self.read_ports: + temp.append("p_en_bar{0}".format(port)) for port in self.write_ports: temp.append("w_en{0}".format(port)) for port in self.all_ports: - temp.append("clk_buf_bar{0}".format(port)) - temp.append("clk_buf{0}".format(port)) + temp.append("wl_en{0}".format(port)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -403,16 +409,21 @@ class sram_base(design): mod = self.control_logic_r insts.append(self.add_inst(name="control{}".format(port), mod=mod)) - + + # Inputs temp = ["csb{}".format(port)] if port in self.readwrite_ports: temp.append("web{}".format(port)) temp.append("clk{}".format(port)) + + # Ouputs if port in self.read_ports: temp.append("s_en{}".format(port)) if port in self.write_ports: temp.append("w_en{}".format(port)) - temp.extend(["clk_buf_bar{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"]) + if port in self.read_ports: + temp.append("p_en_bar{}".format(port)) + temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"]) self.connect_inst(temp) return insts diff --git a/compiler/tests/11_dff_inv_test.py b/compiler/tests/04_pand2_test.py similarity index 64% rename from compiler/tests/11_dff_inv_test.py rename to compiler/tests/04_pand2_test.py index 53a92852..68433e96 100755 --- a/compiler/tests/11_dff_inv_test.py +++ b/compiler/tests/04_pand2_test.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Run a regression test on a dff_inv. +Run a regression test on a pand2 cell """ import unittest @@ -11,19 +11,22 @@ import globals from globals import OPTS import debug -class dff_inv_test(openram_test): +class pand2_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import dff_inv + global verify + import verify - debug.info(2, "Testing dff_inv 4x") - a = dff_inv.dff_inv(4) + import pand2 + + debug.info(2, "Testing pand2 gate 4x") + a = pand2.pand2(4) self.local_check(a) globals.end_openram() -# run the test from the command line +# instantiate a copdsay of the class to actually run the test if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pbuf_test.py b/compiler/tests/04_pbuf_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/11_dff_inv_array_test.py b/compiler/tests/11_dff_inv_array_test.py deleted file mode 100755 index ed03e6bc..00000000 --- a/compiler/tests/11_dff_inv_array_test.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -""" -Run a regression test on a dff_array. -""" - -import unittest -from testutils import header,openram_test -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -from globals import OPTS -import debug - -class dff_inv_array_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import dff_inv_array - - debug.info(2, "Testing dff_inv_array for 3x3") - a = dff_inv_array.dff_inv_array(rows=3, columns=3) - self.local_check(a) - - debug.info(2, "Testing dff_inv_array for 1x3") - a = dff_inv_array.dff_inv_array(rows=1, columns=3) - self.local_check(a) - - debug.info(2, "Testing dff_inv_array for 3x1") - a = dff_inv_array.dff_inv_array(rows=3, columns=1) - self.local_check(a) - - globals.end_openram() - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main() diff --git a/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py new file mode 100755 index 00000000..4560f939 --- /dev/null +++ b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete") +class psram_1bank_2mux_1rw_1w_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 0 + + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + c.num_words=32 + c.words_per_row=2 + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py index 223cb6ed..be654d6a 100755 --- a/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py @@ -11,8 +11,8 @@ import globals from globals import OPTS import debug -#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete") -class psram_1bank_2mux_test(openram_test): +#@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error") +class psram_1bank_2mux_1w_1r_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) @@ -30,7 +30,13 @@ class psram_1bank_2mux_test(openram_test): num_banks=1) c.num_words=32 c.words_per_row=2 - debug.info(1, "Single bank two way column mux 1w/1r with control logic") + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) a = sram(c, "sram") self.local_check(a, final_verification=True) diff --git a/compiler/tests/20_psram_1bank_2mux_test.py b/compiler/tests/20_psram_1bank_2mux_test.py index e382eac4..bf5b9585 100755 --- a/compiler/tests/20_psram_1bank_2mux_test.py +++ b/compiler/tests/20_psram_1bank_2mux_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete") +@unittest.skip("SKIPPING 20_psram_1bank_2mux_test, wide metal supply routing error") class psram_1bank_2mux_test(openram_test): def runTest(self): @@ -31,7 +31,13 @@ class psram_1bank_2mux_test(openram_test): num_banks=1) c.num_words=32 c.words_per_row=2 - debug.info(1, "Single bank two way column mux with control logic") + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) a = sram(c, "sram") self.local_check(a, final_verification=True) diff --git a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py new file mode 100755 index 00000000..0a40352a --- /dev/null +++ b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class psram_1bank_4mux_1rw_1r_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 1 + + c = sram_config(word_size=4, + num_words=64, + num_banks=1) + c.num_words=64 + c.words_per_row=4 + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py index 49fd47be..69a8def5 100755 --- a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py @@ -29,7 +29,13 @@ class sram_1bank_2mux_1rw_1r_test(openram_test): num_banks=1) c.words_per_row=2 - debug.info(1, "Single bank, two way column mux 1rw, 1r with control logic") + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) a = sram(c, "sram") self.local_check(a, final_verification=True) diff --git a/compiler/tests/20_sram_1bank_2mux_test.py b/compiler/tests/20_sram_1bank_2mux_test.py index 2c8e28f0..769c7a51 100755 --- a/compiler/tests/20_sram_1bank_2mux_test.py +++ b/compiler/tests/20_sram_1bank_2mux_test.py @@ -23,7 +23,13 @@ class sram_1bank_2mux_test(openram_test): num_banks=1) c.words_per_row=2 - debug.info(1, "Single bank two way column mux with control logic") + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) a = sram(c, "sram") self.local_check(a, final_verification=True) diff --git a/compiler/tests/20_sram_1bank_4mux_test.py b/compiler/tests/20_sram_1bank_4mux_test.py index 489ff354..2370b411 100755 --- a/compiler/tests/20_sram_1bank_4mux_test.py +++ b/compiler/tests/20_sram_1bank_4mux_test.py @@ -23,7 +23,13 @@ class sram_1bank_4mux_test(openram_test): num_banks=1) c.words_per_row=4 - debug.info(1, "Single bank, four way column mux with control logic") + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) a = sram(c, "sram") self.local_check(a, final_verification=True) diff --git a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py new file mode 100755 index 00000000..75e14d3c --- /dev/null +++ b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class sram_1bank_8mux_1rw_1r_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + c = sram_config(word_size=2, + num_words=128, + num_banks=1) + + c.words_per_row=8 + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/20_sram_1bank_8mux_test.py b/compiler/tests/20_sram_1bank_8mux_test.py index 2595582f..f2ad684a 100755 --- a/compiler/tests/20_sram_1bank_8mux_test.py +++ b/compiler/tests/20_sram_1bank_8mux_test.py @@ -23,7 +23,13 @@ class sram_1bank_8mux_test(openram_test): num_banks=1) c.words_per_row=8 - debug.info(1, "Single bank, eight way column mux with control logic") + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) a = sram(c, "sram") self.local_check(a, final_verification=True) diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py index 673dcbca..631e0309 100755 --- a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py @@ -29,7 +29,13 @@ class sram_1bank_nomux_1rw_1r_test(openram_test): num_banks=1) c.words_per_row=1 - debug.info(1, "Single bank, no column mux 1rw, 1r with control logic") + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) a = sram(c, "sram") self.local_check(a, final_verification=True) diff --git a/compiler/tests/20_sram_1bank_nomux_test.py b/compiler/tests/20_sram_1bank_nomux_test.py index 783bcad2..2afa9ae8 100755 --- a/compiler/tests/20_sram_1bank_nomux_test.py +++ b/compiler/tests/20_sram_1bank_nomux_test.py @@ -23,7 +23,13 @@ class sram_1bank_nomux_test(openram_test): num_banks=1) c.words_per_row=1 - debug.info(1, "Single bank, no column mux with control logic") + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) a = sram(c, "sram") self.local_check(a, final_verification=True) diff --git a/compiler/tests/20_sram_4bank_test.py b/compiler/tests/20_sram_4bank_test.py deleted file mode 100755 index 25649e8e..00000000 --- a/compiler/tests/20_sram_4bank_test.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 -""" -Run a regression test on a 4 bank SRAM -""" - -import unittest -from testutils import header,openram_test -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -from globals import OPTS -import debug - -@unittest.skip("Multibank is not working yet.") -class sram_4bank_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram - from sram_config import sram_config - c = sram_config(word_size=16, - num_words=64, - num_banks=4) - - debug.info(1, "Four bank, no column mux with control logic") - a = sram(c, "sram1") - self.local_check(a, final_verification=True) - - c.num_words=128 - c.words_per_row=2 - debug.info(1, "Four bank two way column mux with control logic") - a = sram(c, "sram2") - self.local_check(a, final_verification=True) - - c.num_words=256 - c.words_per_row=4 - debug.info(1, "Four bank, four way column mux with control logic") - a = sram(c, "sram3") - self.local_check(a, final_verification=True) - - c.word_size=2 - c.num_words=256 - c.words_per_row=8 - debug.info(1, "Four bank, eight way column mux with control logic") - a = sram.sram(c, "sram4") - self.local_check(a, final_verification=True) - - globals.end_openram() - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main() diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index 5facb482..f88f1364 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -49,29 +49,28 @@ class timing_sram_test(openram_test): #Combine info about port into all data data.update(port_data[0]) - #Assumes single rw port (6t sram) if OPTS.tech_name == "freepdk45": - golden_data = {'delay_hl': [2.5829000000000004], - 'delay_lh': [0.2255964], - 'leakage_power': 0.0019498999999999996, + golden_data = {'delay_hl': [2.6232], + 'delay_lh': [0.2775342], + 'leakage_power': 0.0020258999999999997, 'min_period': 4.844, - 'read0_power': [0.055371399999999994], - 'read1_power': [0.0520225], - 'slew_hl': [0.0794261], - 'slew_lh': [0.0236264], - 'write0_power': [0.06545659999999999], - 'write1_power': [0.057846299999999996]} + 'read0_power': [0.0557804], + 'read1_power': [0.0525619], + 'slew_hl': [0.1082014], + 'slew_lh': [0.0238257], + 'write0_power': [0.0456528], + 'write1_power': [0.0442747]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_hl': [3.452], - 'delay_lh': [1.3792000000000002], - 'leakage_power': 0.0257065, - 'min_period': 4.688, - 'read0_power': [15.0755], - 'read1_power': [14.4526], - 'slew_hl': [0.6137363], - 'slew_lh': [0.3381045], - 'write0_power': [16.9203], - 'write1_power': [15.367]} + golden_data = {'delay_hl': [6.079300000000001], + 'delay_lh': [1.7767000000000002], + 'leakage_power': 0.026282499999999997, + 'min_period': 9.375, + 'read0_power': [6.5802], + 'read1_power': [6.2815], + 'slew_hl': [0.7396921999999999], + 'slew_lh': [0.3397355], + 'write0_power': [5.7337], + 'write1_power': [5.8691]} else: self.assertTrue(False) # other techs fail # Check if no too many or too few results diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index e203b878..47c72e78 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -50,27 +50,27 @@ class timing_sram_test(openram_test): data.update(port_data[0]) if OPTS.tech_name == "freepdk45": - golden_data = {'delay_hl': [2.584251], - 'delay_lh': [0.22870469999999998], - 'leakage_power': 0.0009567935, + golden_data = {'delay_hl': [2.625351], + 'delay_lh': [0.28080869999999997], + 'leakage_power': 0.001040682, 'min_period': 4.844, - 'read0_power': [0.0547588], - 'read1_power': [0.051159970000000006], - 'slew_hl': [0.08164099999999999], - 'slew_lh': [0.025474979999999998], - 'write0_power': [0.06513271999999999], - 'write1_power': [0.058057000000000004]} + 'read0_power': [0.0553667], + 'read1_power': [0.05177618], + 'slew_hl': [0.1099853], + 'slew_lh': [0.02568626], + 'write0_power': [0.04517803], + 'write1_power': [0.04449207]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_hl': [3.644147], - 'delay_lh': [1.629815], - 'leakage_power': 0.001542964, - 'min_period': 4.688, - 'read0_power': [16.28732], - 'read1_power': [15.75155], - 'slew_hl': [0.6722473], - 'slew_lh': [0.3386347], - 'write0_power': [18.545450000000002], - 'write1_power': [16.81084]} + golden_data = {'delay_hl': [6.45408], + 'delay_lh': [2.0787519999999997], + 'leakage_power': 0.001177846, + 'min_period': 9.688, + 'read0_power': [7.088419], + 'read1_power': [6.824107000000001], + 'slew_hl': [0.7980976999999999], + 'slew_lh': [0.3393389], + 'write0_power': [5.982207], + 'write1_power': [6.28866]} else: self.assertTrue(False) # other techs fail diff --git a/compiler/tests/22_psram_1bank_2mux_func_test.py b/compiler/tests/22_psram_1bank_2mux_func_test.py index f2679c03..19d4ab58 100755 --- a/compiler/tests/22_psram_1bank_2mux_func_test.py +++ b/compiler/tests/22_psram_1bank_2mux_func_test.py @@ -11,8 +11,8 @@ import globals from globals import OPTS import debug -#@unittest.skip("SKIPPING 22_psram_1bank_2mux_func_test") -class psram_1bank_2mux_func_test(openram_test): +@unittest.skip("SKIPPING 22_psram_1bank_2mux_1rw_1r_1w_func_test, third port reads are broken?") +class psram_1bank_2mux_1rw_1r_1w_func_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) @@ -35,10 +35,13 @@ class psram_1bank_2mux_func_test(openram_test): num_words=64, num_banks=1) c.words_per_row=2 - debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) + debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) s = sram(c, name="sram") tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/22_psram_1bank_4mux_func_test.py b/compiler/tests/22_psram_1bank_4mux_func_test.py index a8d6dab2..dc6fffff 100755 --- a/compiler/tests/22_psram_1bank_4mux_func_test.py +++ b/compiler/tests/22_psram_1bank_4mux_func_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -#@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test") +@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test, third port reads are broken?") class psram_1bank_4mux_func_test(openram_test): def runTest(self): @@ -35,10 +35,13 @@ class psram_1bank_4mux_func_test(openram_test): num_words=256, num_banks=1) c.words_per_row=4 - debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) + debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) s = sram(c, name="sram") tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/22_psram_1bank_8mux_func_test.py b/compiler/tests/22_psram_1bank_8mux_func_test.py index 1a0a1ec5..af5e6abc 100755 --- a/compiler/tests/22_psram_1bank_8mux_func_test.py +++ b/compiler/tests/22_psram_1bank_8mux_func_test.py @@ -35,10 +35,13 @@ class psram_1bank_8mux_func_test(openram_test): num_words=256, num_banks=1) c.words_per_row=8 - debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) + debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) s = sram(c, name="sram") tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/22_psram_1bank_nomux_func_test.py b/compiler/tests/22_psram_1bank_nomux_func_test.py index 7817b055..8007b6f1 100755 --- a/compiler/tests/22_psram_1bank_nomux_func_test.py +++ b/compiler/tests/22_psram_1bank_nomux_func_test.py @@ -35,10 +35,13 @@ class psram_1bank_nomux_func_test(openram_test): num_words=32, num_banks=1) c.words_per_row=1 - debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) + debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) s = sram(c, name="sram") tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/30_openram_test.py b/compiler/tests/30_openram_test.py index afa60216..985eb4b1 100755 --- a/compiler/tests/30_openram_test.py +++ b/compiler/tests/30_openram_test.py @@ -45,13 +45,14 @@ class openram_test(openram_test): OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) - - cmd_string = "python3 {0}/openram.py -n -o {1} -p {2} {3} {0}/tests/config_20_{4}.py 2>&1 > {2}/output.log" - cmd = cmd_string.format(OPENRAM_HOME, - out_file, - out_path, - opts, - OPTS.tech_name) + # Always perform code coverage + exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) + cmd = "{0} -n -o {1} -p {2} {3} config_20_{4}.py 2>&1 > {5}/output.log".format(exe_name, + out_file, + out_path, + verbosity, + OPTS.tech_name, + out_path) debug.info(1, cmd) os.system(cmd) diff --git a/technology/freepdk45/gds_lib/cell_1rw_1r.gds b/technology/freepdk45/gds_lib/cell_1rw_1r.gds index fe12fc72..00dc1855 100644 Binary files a/technology/freepdk45/gds_lib/cell_1rw_1r.gds and b/technology/freepdk45/gds_lib/cell_1rw_1r.gds differ diff --git a/technology/freepdk45/gds_lib/replica_cell_1rw_1r.gds b/technology/freepdk45/gds_lib/replica_cell_1rw_1r.gds index 8bc45cbb..96bf3d75 100644 Binary files a/technology/freepdk45/gds_lib/replica_cell_1rw_1r.gds and b/technology/freepdk45/gds_lib/replica_cell_1rw_1r.gds differ diff --git a/technology/scn3me_subm/sp_lib/write_driver.sp b/technology/scn3me_subm/sp_lib/write_driver.sp index edddf18c..88f80361 100644 --- a/technology/scn3me_subm/sp_lib/write_driver.sp +++ b/technology/scn3me_subm/sp_lib/write_driver.sp @@ -2,34 +2,35 @@ .SUBCKT write_driver din bl br en vdd gnd **** Inverter to conver Data_in to data_in_bar ****** -M_1 din_bar din gnd gnd n W='1.2*1u' L=0.6u -M_2 din_bar din vdd vdd p W='2.1*1u' L=0.6u +* din_bar = inv(din) +M_1 din_bar din gnd gnd n W=1.2u L=0.6u +M_2 din_bar din vdd vdd p W=2.1u L=0.6u **** 2input nand gate follwed by inverter to drive BL ****** -M_3 din_bar_gated en net_7 gnd n W='2.1*1u' L=0.6u -M_4 net_7 din gnd gnd n W='2.1*1u' L=0.6u -M_5 din_bar_gated en vdd vdd p W='2.1*1u' L=0.6u -M_6 din_bar_gated din vdd vdd p W='2.1*1u' L=0.6u - - -M_7 net_1 din_bar_gated vdd vdd p W='2.1*1u' L=0.6u -M_8 net_1 din_bar_gated gnd gnd n W='1.2*1u' L=0.6u +* din_bar_gated = nand(en, din) +M_3 din_bar_gated en net_7 gnd n W=2.1u L=0.6u +M_4 net_7 din gnd gnd n W=2.1u L=0.6u +M_5 din_bar_gated en vdd vdd p W=2.1u L=0.6u +M_6 din_bar_gated din vdd vdd p W=2.1u L=0.6u +* din_bar_gated_bar = inv(din_bar_gated) +M_7 din_bar_gated_bar din_bar_gated vdd vdd p W=2.1u L=0.6u +M_8 din_bar_gated_bar din_bar_gated gnd gnd n W=1.2u L=0.6u **** 2input nand gate follwed by inverter to drive BR****** - -M_9 din_gated en vdd vdd p W='2.1*1u' L=0.6u -M_10 din_gated en net_8 gnd n W='2.1*1u' L=0.6u -M_11 net_8 din_bar gnd gnd n W='2.1*1u' L=0.6u -M_12 din_gated din_bar vdd vdd p W='2.1*1u' L=0.6u - -M_13 net_6 din_gated vdd vdd p W='2.1*1u' L=0.6u -M_14 net_6 din_gated gnd gnd n W='1.2*1u' L=0.6u +* din_gated = nand(en, din_bar) +M_9 din_gated en vdd vdd p W=2.1u L=0.6u +M_10 din_gated en net_8 gnd n W=2.1u L=0.6u +M_11 net_8 din_bar gnd gnd n W=2.1u L=0.6u +M_12 din_gated din_bar vdd vdd p W=2.1u L=0.6u +* din_gated_bar = inv(din_gated) +M_13 din_gated_bar din_gated vdd vdd p W=2.1u L=0.6u +M_14 din_gated_bar din_gated gnd gnd n W=1.2u L=0.6u ************************************************ - -M_15 bl net_6 net_5 gnd n W='3.6*1u' L=0.6u -M_16 br net_1 net_5 gnd n W='3.6*1u' L=0.6u -M_17 net_5 en gnd gnd n W='3.6*1u' L=0.6u +* pull down with en enable +M_15 bl din_gated_bar net_5 gnd n W=3.6u L=0.6u +M_16 br din_bar_gated_bar net_5 gnd n W=3.6u L=0.6u +M_17 net_5 en gnd gnd n W=3.6u L=0.6u diff --git a/technology/scn4m_subm/sp_lib/write_driver.sp b/technology/scn4m_subm/sp_lib/write_driver.sp index afcf1049..d1dbf9b2 100644 --- a/technology/scn4m_subm/sp_lib/write_driver.sp +++ b/technology/scn4m_subm/sp_lib/write_driver.sp @@ -1,23 +1,37 @@ *********************** Write_Driver ****************************** .SUBCKT write_driver din bl br en vdd gnd -* SPICE3 file created from write_driver.ext - technology: scmos -M1000 a_44_708# a_36_700# bl gnd n w=2.4u l=0.4u -M1001 br a_16_500# a_44_708# gnd n w=2.4u l=0.4u -M1002 a_44_708# en gnd gnd n w=2.4u l=0.4u -M1003 gnd a_8_284# a_16_500# gnd n w=0.8u l=0.4u -M1004 a_36_700# a_20_328# gnd gnd n w=0.8u l=0.4u -M1005 vdd a_8_284# a_16_500# vdd p w=1.4u l=0.4u -M1006 a_36_700# a_20_328# vdd vdd p w=1.4u l=0.4u -M1007 vdd en a_20_328# vdd p w=1.4u l=0.4u -M1008 a_20_328# a_64_360# vdd vdd p w=1.4u l=0.4u -M1009 a_48_328# en a_20_328# gnd n w=1.4u l=0.4u -M1010 gnd a_64_360# a_48_328# gnd n w=1.4u l=0.4u -M1011 a_40_228# en a_8_284# gnd n w=1.4u l=0.4u -M1012 gnd din a_40_228# gnd n w=1.4u l=0.4u -M1013 a_64_360# din gnd gnd n w=0.8u l=0.4u -M1014 a_8_284# en vdd vdd p w=1.4u l=0.4u -M1015 vdd din a_8_284# vdd p w=1.4u l=0.4u -M1016 a_64_360# din vdd vdd p w=1.4u l=0.4u +**** Inverter to conver Data_in to data_in_bar ****** +* din_bar = inv(din) +M_1 din_bar din gnd gnd n W=0.8u L=0.4u +M_2 din_bar din vdd vdd p W=1.4u L=0.4u -.ENDS +**** 2input nand gate follwed by inverter to drive BL ****** +* din_bar_gated = nand(en, din) +M_3 din_bar_gated en net_7 gnd n W=1.4u L=0.4u +M_4 net_7 din gnd gnd n W=1.4u L=0.4u +M_5 din_bar_gated en vdd vdd p W=1.4u L=0.4u +M_6 din_bar_gated din vdd vdd p W=1.4u L=0.4u +* din_bar_gated_bar = inv(din_bar_gated) +M_7 din_bar_gated_bar din_bar_gated vdd vdd p W=1.4u L=0.4u +M_8 din_bar_gated_bar din_bar_gated gnd gnd n W=0.8u L=0.4u + +**** 2input nand gate follwed by inverter to drive BR****** +* din_gated = nand(en, din_bar) +M_9 din_gated en vdd vdd p W=1.4u L=0.4u +M_10 din_gated en net_8 gnd n W=1.4u L=0.4u +M_11 net_8 din_bar gnd gnd n W=1.4u L=0.4u +M_12 din_gated din_bar vdd vdd p W=1.4u L=0.4u +* din_gated_bar = inv(din_gated) +M_13 din_gated_bar din_gated vdd vdd p W=1.4u L=0.4u +M_14 din_gated_bar din_gated gnd gnd n W=0.8u L=0.4u + +************************************************ +* pull down with en enable +M_15 bl din_gated_bar net_5 gnd n W=2.4u L=0.4u +M_16 br din_bar_gated_bar net_5 gnd n W=2.4u L=0.4u +M_17 net_5 en gnd gnd n W=2.4u L=0.4u + + + +.ENDS $ write_driver diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 25afd844..323add70 100755 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -240,7 +240,7 @@ spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+" #spice stimulus related variables -spice["feasible_period"] = 5 # estimated feasible period in ns +spice["feasible_period"] = 10 # estimated feasible period in ns spice["supply_voltages"] = [4.5, 5.0, 5.5] # Supply voltage corners in [Volts] spice["nom_supply_voltage"] = 5.0 # Nominal supply voltage in [Volts] spice["rise_time"] = 0.05 # rise time in [Nano-seconds]