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/Makefile b/compiler/Makefile index af3eb2c0..a547b6ed 100644 --- a/compiler/Makefile +++ b/compiler/Makefile @@ -1,93 +1,45 @@ +TECH = scn4m_subm CUR_DIR = $(shell pwd) TEST_DIR = ${CUR_DIR}/tests -MAKEFLAGS += -j 2 +MAKEFLAGS += -j 1 # Library test -LIBRARY_TESTS = \ -01_library_drc_test.py \ -02_library_lvs_test.py +LIBRARY_TESTS = $(shell find ${TEST_DIR} -name 0[1-2]*_test.py) # Technology and DRC tests (along with ptx) -TECH_TESTS = \ -03_contact_test.py \ -03_ptx_1finger_pmos_test.py \ -03_ptx_4finger_nmos_test.py \ -03_path_test.py \ -03_ptx_3finger_nmos_test.py \ -03_ptx_4finger_pmos_test.py \ -03_ptx_1finger_nmos_test.py \ -03_ptx_3finger_pmos_test.py \ -03_wire_test.py +TECH_TESTS = $(shell find ${TEST_DIR} -name 03*_test.py) # Parameterized cells -PCELLS_TESTS = \ -04_pinv_1x_test.py \ -04_pinv_1x_beta_test.py \ -04_pinv_2x_test.py \ -04_pinv_10x_test.py \ -04_pnand2_test.py \ -04_pnor2_test.py \ -04_pnand3_test.py\ -04_wordline_driver_test.py \ -04_precharge_test.py +CELL_TESTS = $(shell find ${TEST_DIR} -name 04*_test.py) # Dynamically generated modules and arrays -MODULE_TESTS = \ -05_bitcell_array_test.py \ -06_hierarchical_decoder_test.py \ -06_hierarchical_predecode2x4_test.py \ -06_hierarchical_predecode3x8_test.py \ -07_single_level_column_mux_array_test.py \ -08_precharge_array_test.py \ -09_sense_amp_array_test.py \ -10_write_driver_array_test.py \ -11_ms_flop_array_test.py \ -12_tri_gate_array_test.py \ -13_delay_chain_test.py \ -14_replica_bitline_test.py \ -16_control_logic_test.py +MODULE_TESTS = $(shell find ${TEST_DIR} -name 0[5-9]*_test.py)\ +$(shell find ${TEST_DIR} -name 1*_test.py) # Top-level SRAM configurations -TOP_TESTS = \ -19_multi_bank_test.py \ -19_single_bank_test.py \ -20_sram_1bank_test.py \ -20_sram_2bank_test.py \ -20_sram_4bank_test.py +TOP_TESTS = $(shell find ${TEST_DIR} -name 20*_test.py) # All simulation tests. -CHAR_TESTS = \ -21_hspice_delay_test.py \ -21_ngspice_delay_test.py \ -21_ngspice_setuphold_test.py \ -21_hspice_setuphold_test.py \ -22_sram_func_test.py \ -22_pex_func_test_with_pinv.py \ -23_lib_sram_prune_test.py \ -23_lib_sram_test.py +CHAR_TESTS = $(shell find ${TEST_DIR} -name 2[1-2]*_test.py) # Keep the model lib test here since it is fast # and doesn't need simulation. -USAGE_TESTS = \ -23_lib_sram_model_test.py \ -24_lef_sram_test.py \ -25_verilog_sram_test.py +USAGE_TESTS = $(shell find ${TEST_DIR} -name 2[3-9]*_test.py)\ +$(shell find ${TEST_DIR} -name 30*_test.py) -ALL_FILES = \ +ALL_TESTS = \ ${LIBRARY_TESTS} \ ${TECH_TESTS} \ -${PCELLS_TESTS} \ -${MODULES_TESTS} \ +${CELL_TESTS} \ +${MODULE_TESTS} \ ${TOP_TESTS} \ ${CHAR_TESTS} \ ${USAGE_TESTS} -default all: +.PHONY: ${ALL_TESTS} -$(ALL_FILES): - python ${TEST_DIR}/$@ -t freepdk45 - python ${TEST_DIR}/$@ -t scn3me_subm +all: ${ALL_TESTS} # Library tests lib: ${LIBRARY_TESTS} @@ -96,10 +48,10 @@ lib: ${LIBRARY_TESTS} tech: ${TECH_TESTS} # Dynamically generated cells -pcells: ${PCELLS_TESTS} +cell: ${CELL_TESTS} # Dynamically generated modules -modules: ${MODULES_TESTS} +module: ${MODULE_TESTS} # Top level SRAM tests top: ${TOP_TESTS} @@ -110,6 +62,9 @@ char: ${CHAR_TESTS} # Usage and file generation usage: ${USAGE_TESTS} +$(ALL_TESTS): + python3 $@ -t ${TECH} + clean: find . -name \*.pyc -exec rm {} \; find . -name \*~ -exec rm {} \; 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)= min(p[0], r[0]) and \ - q[1] <= max(p[1], r[1]) and \ - q[1] >= min(p[1], r[1]): + if q.x <= max(p.x, r.x) and \ + q.x >= min(p.x, r.x) and \ + q.y <= max(p.y, r.y) and \ + q.y >= min(p.y, r.y): return True return False @@ -473,8 +473,8 @@ class pin_layout: x = (b2*c1 - b1*c2)/determinant y = (a1*c2 - a2*c1)/determinant - r = [x,y] + r = vector(x,y).snap_to_grid() if self.on_segment(a, r, b) and self.on_segment(c, r, d): - return [x, y] + return r return None diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index d858b609..d9d2ef7a 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -80,7 +80,7 @@ class pbitcell(design.design): self.offset_all_coordinates() gnd_overlap = vector(0, 0.5*contact.well.width) self.translate_all(gnd_overlap) - self.DRC_LVS() + def add_pins(self): """ add pins and set names for bitlines and wordlines """ @@ -323,20 +323,21 @@ class pbitcell(design.design): # Add rails for vdd and gnd gnd_ypos = self.rowline_offset - self.total_ports*self.rowline_spacing self.gnd_position = vector(0, gnd_ypos) - self.gnd = self.add_layout_pin_rect_center(text="gnd", - layer="metal1", - offset=self.gnd_position, - width=self.width, - height=self.m1_width) - + self.add_rect_center(layer="metal1", + offset=self.gnd_position, + width=self.width, + height=self.m1_width) + self.add_power_pin("gnd", vector(0,gnd_ypos)) + + vdd_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset self.vdd_position = vector(0, vdd_ypos) - self.vdd = self.add_layout_pin_rect_center(text="vdd", - layer="metal1", - offset=self.vdd_position, - width=self.width, - height=self.m1_width) - + self.add_rect_center(layer="metal1", + offset=self.vdd_position, + width=self.width, + height=self.m1_width) + self.add_power_pin("vdd", vector(0,vdd_ypos)) + def create_readwrite_ports(self): """ Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell. diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index b99e644c..5255621b 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -29,6 +29,7 @@ class functional(simulation): self.set_spice_constants() self.set_stimulus_variables() self.create_signal_names() + # Number of checks can be changed self.num_cycles = 2 @@ -141,17 +142,17 @@ class functional(simulation): sp_read_value = "" for bit in range(self.word_size): value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check)) - if value > 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/globals.py b/compiler/globals.py index 87763422..5886d726 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -59,7 +59,7 @@ def parse_args(): # This may be overridden when we read a config file though... if OPTS.tech_name == "": OPTS.tech_name = "scmos" - # Alias SCMOS to AMI 0.5um + # Alias SCMOS to 180nm if OPTS.tech_name == "scmos": OPTS.tech_name = "scn4m_subm" @@ -89,6 +89,7 @@ def print_banner(): print("|=========" + dev_info.center(60) + "=========|") temp_info = "Temp dir: {}".format(OPTS.openram_temp) print("|=========" + temp_info.center(60) + "=========|") + print("|=========" + "See LICENSE for license info".center(60) + "=========|") print("|==============================================================================|") @@ -412,6 +413,9 @@ def report_status(): print("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size, OPTS.num_words, OPTS.num_banks)) + print("RW ports: {0}\nR-only ports: {1}\nW-only ports: {2}".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports)) if OPTS.netlist_only: print("Netlist only mode (no physical design is being done).") 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/openram.py b/compiler/openram.py index ee43749f..703a8402 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -26,6 +26,10 @@ if len(args) != 1: # These depend on arguments, so don't load them until now. import debug +# Keep track of running stats +start_time = datetime.datetime.now() +print_time("Start",start_time) + init_openram(config_file=args[0], is_unit_test=False) # Only print banner here so it's not in unit tests @@ -34,10 +38,14 @@ print_banner() # Output info about this run report_status() -# Start importing design modules after we have the config file -import verify -from sram import sram from sram_config import sram_config + + +# Configure the SRAM organization +c = sram_config(word_size=OPTS.word_size, + num_words=OPTS.num_words) +print("Words per row: {}".format(c.words_per_row)) + #from parser import * output_extensions = ["sp","v","lib"] if OPTS.datasheet_gen: @@ -48,15 +56,8 @@ output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in output_extensions] print("Output files are: ") print(*output_files,sep="\n") -# Keep track of running stats -start_time = datetime.datetime.now() -print_time("Start",start_time) -# Configure the SRAM organization -c = sram_config(word_size=OPTS.word_size, - num_words=OPTS.num_words) - -# import SRAM test generation +from sram import sram s = sram(sram_config=c, name=OPTS.output_name) 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/router/pin_group.py b/compiler/router/pin_group.py index e50f94d9..13368d12 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -172,7 +172,6 @@ class pin_group: ymax = max(plc.y,elc.y) ll = vector(plc.x, ymin) ur = vector(prc.x, ymax) - p = pin_layout(pin.name, [ll, ur], pin.layer) elif pin.yoverlaps(enclosure): # Is it horizontal overlap, extend pin shape to enclosure pbc = pin.bc() @@ -183,7 +182,6 @@ class pin_group: xmax = max(pbc.x,ebc.x) ll = vector(xmin, pbc.y) ur = vector(xmax, puc.y) - p = pin_layout(pin.name, [ll, ur], pin.layer) else: # Neither, so we must do a corner-to corner pc = pin.center() @@ -194,8 +192,10 @@ class pin_group: ymax = max(pc.y, ec.y) ll = vector(xmin, ymin) ur = vector(xmax, ymax) - p = pin_layout(pin.name, [ll, ur], pin.layer) + if ll.x==ur.x or ll.y==ur.y: + return None + p = pin_layout(pin.name, [ll, ur], pin.layer) return p def find_above_connector(self, pin, enclosures): @@ -226,7 +226,7 @@ class pin_group: # If it already overlaps, no connector needed if above_item.overlaps(pin): return None - + # Otherwise, make a connector to the item p = self.compute_connector(pin, above_item) return p @@ -485,7 +485,9 @@ class pin_group: for pin_list in self.pins: if not self.overlap_any_shape(pin_list, self.enclosures): connector = self.find_smallest_connector(pin_list, self.enclosures) - debug.check(connector!=None, "Could not find a connector for {} with {}".format(pin_list, self.enclosures)) + if connector==None: + debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures)) + self.router.write_debug_gds("no_connector.gds") self.enclosures.append(connector) @@ -559,15 +561,13 @@ class pin_group: of any grid in the other set. """ # We could optimize this to just check the boundaries - g1_grids = set() - g2_grids = set() + adj_grids = set() for g1 in self.grids: for g2 in other.grids: if g1.distance(g2) <= separation: - g1_grids.add(g1) - g2_grids.add(g2) + adj_grids.add(g1) - return g1_grids,g2_grids + return adj_grids def convert_pin(self): """ @@ -576,14 +576,16 @@ class pin_group: should be either blocked or part of the pin. """ pin_set = set() + partial_set = set() blockage_set = set() for pin_list in self.pins: for pin in pin_list: debug.info(2," Converting {0}".format(pin)) # Determine which tracks the pin overlaps - pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin) - pin_set.update(pin_in_tracks) + (sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin) + pin_set.update(sufficient) + partial_set.update(insufficient) # Blockages will be a super-set of pins since it uses the inflated pin shape. blockage_in_tracks = self.router.convert_blockage(pin) @@ -595,89 +597,94 @@ class pin_group: if len(shared_set)>0: debug.info(2,"Removing pins {}".format(shared_set)) pin_set.difference_update(shared_set) + shared_set = partial_set & self.router.blocked_grids + if len(shared_set)>0: + debug.info(2,"Removing pins {}".format(shared_set)) + partial_set.difference_update(shared_set) shared_set = blockage_set & self.router.blocked_grids if len(shared_set)>0: debug.info(2,"Removing blocks {}".format(shared_set)) blockage_set.difference_update(shared_set) # At least one of the groups must have some valid tracks - if (len(pin_set)==0 and len(blockage_set)==0): + if (len(pin_set)==0 and len(partial_set)==0 and len(blockage_set)==0): #debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins)) for pin_list in self.pins: for pin in pin_list: - debug.info(2," Converting {0}".format(pin)) + debug.warning(" Expanding conversion {0}".format(pin)) # Determine which tracks the pin overlaps - pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin, expansion=1) - pin_set.update(pin_in_tracks) + (sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin, expansion=1) + pin_set.update(sufficient) + partial_set.update(insufficient) - if len(pin_set)==0: + if len(pin_set)==0 and len(partial_set)==0: debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins)) self.router.write_debug_gds("blocked_pin.gds") - # We need to route each of the components, so don't combine the groups - self.grids = pin_set | blockage_set - # Remember the secondary grids for removing adjacent pins in wide metal spacing - self.secondary_grids = blockage_set - pin_set + # Consider all the grids that would be blocked + self.grids = pin_set | partial_set + # Remember the secondary grids for removing adjacent pins + self.secondary_grids = partial_set debug.info(2," pins {}".format(self.grids)) debug.info(2," secondary {}".format(self.secondary_grids)) - def recurse_simple_overlap_enclosure(self, start_set, direct): - """ - Recursive function to return set of tracks that connects to - the actual supply rail wire in a given direction (or terminating - when any track is no longer in the supply rail. - """ - next_set = grid_utils.expand_border(start_set, direct) + # def recurse_simple_overlap_enclosure(self, start_set, direct): + # """ + # Recursive function to return set of tracks that connects to + # the actual supply rail wire in a given direction (or terminating + # when any track is no longer in the supply rail. + # """ + # next_set = grid_utils.expand_border(start_set, direct) - supply_tracks = self.router.supply_rail_tracks[self.name] - supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name] + # supply_tracks = self.router.supply_rail_tracks[self.name] + # supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name] - supply_overlap = next_set & supply_tracks - wire_overlap = next_set & supply_wire_tracks + # supply_overlap = next_set & supply_tracks + # wire_overlap = next_set & supply_wire_tracks - # If the rail overlap is the same, we are done, since we connected to the actual wire - if len(wire_overlap)==len(start_set): - new_set = start_set | wire_overlap - # If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region - elif len(supply_overlap)==len(start_set): - recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct) - new_set = start_set | supply_overlap | recurse_set - else: - # If we got no next set, we are done, can't expand! - new_set = set() + # # If the rail overlap is the same, we are done, since we connected to the actual wire + # if len(wire_overlap)==len(start_set): + # new_set = start_set | wire_overlap + # # If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region + # elif len(supply_overlap)==len(start_set): + # recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct) + # new_set = start_set | supply_overlap | recurse_set + # else: + # # If we got no next set, we are done, can't expand! + # new_set = set() - return new_set + # return new_set - def create_simple_overlap_enclosure(self, start_set): - """ - This takes a set of tracks that overlap a supply rail and creates an enclosure - that is ensured to overlap the supply rail wire. - It then adds rectangle(s) for the enclosure. - """ - additional_set = set() - # Check the layer of any element in the pin to determine which direction to route it - e = next(iter(start_set)) - new_set = start_set.copy() - if e.z==0: - new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH) - if not new_set: - new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH) - else: - new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST) - if not new_set: - new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST) + # def create_simple_overlap_enclosure(self, start_set): + # """ + # This takes a set of tracks that overlap a supply rail and creates an enclosure + # that is ensured to overlap the supply rail wire. + # It then adds rectangle(s) for the enclosure. + # """ + # additional_set = set() + # # Check the layer of any element in the pin to determine which direction to route it + # e = next(iter(start_set)) + # new_set = start_set.copy() + # if e.z==0: + # new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH) + # if not new_set: + # new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH) + # else: + # new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST) + # if not new_set: + # new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST) - # Expand the pin grid set to include some extra grids that connect the supply rail - self.grids.update(new_set) + # # Expand the pin grid set to include some extra grids that connect the supply rail + # self.grids.update(new_set) - # Add the inflated set so we don't get wide metal spacing issues (if it exists) - self.blockages.update(grid_utils.inflate_set(new_set,self.router.supply_rail_space_width)) + # # Block the grids + # self.blockages.update(new_set) - # Add the polygon enclosures and set this pin group as routed - self.set_routed() - self.enclosures = self.compute_enclosures() + # # Add the polygon enclosures and set this pin group as routed + # self.set_routed() + # self.enclosures = self.compute_enclosures() diff --git a/compiler/router/router.py b/compiler/router/router.py index bb6e1efc..f6f9df2c 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -21,12 +21,12 @@ class router(router_tech): It populates blockages on a grid class. """ - def __init__(self, layers, design, gds_filename=None): + def __init__(self, layers, design, gds_filename=None, rail_track_width=1): """ This will instantiate a copy of the gds file or the module at (0,0) and route on top of this. The blockages from the gds/module will be considered. """ - router_tech.__init__(self, layers) + router_tech.__init__(self, layers, rail_track_width) self.cell = design @@ -70,8 +70,7 @@ class router(router_tech): self.boundary = self.layout.measureBoundary(self.top_name) # These must be un-indexed to get rid of the matrix type self.ll = vector(self.boundary[0][0], self.boundary[0][1]) - # Pad the UR by a few tracks to add an extra rail possibly - self.ur = vector(self.boundary[1][0], self.boundary[1][1]) + self.track_widths.scale(5,5) + self.ur = vector(self.boundary[1][0], self.boundary[1][1]) def clear_pins(self): """ @@ -130,12 +129,8 @@ class router(router_tech): Pin can either be a label or a location,layer pair: [[x,y],layer]. """ debug.info(1,"Finding pins for {}.".format(pin_name)) - #start_time = datetime.now() self.retrieve_pins(pin_name) - #print_time("Retrieved pins",datetime.now(), start_time) - #start_time = datetime.now() self.analyze_pins(pin_name) - #print_time("Analyzed pins",datetime.now(), start_time) def find_blockages(self): """ @@ -160,48 +155,32 @@ class router(router_tech): # This will get all shapes as blockages and convert to grid units # This ignores shapes that were pins - #start_time = datetime.now() self.find_blockages() - #print_time("Find blockags",datetime.now(), start_time) # Convert the blockages to grid units - #start_time = datetime.now() self.convert_blockages() - #print_time("Find blockags",datetime.now(), start_time) # This will convert the pins to grid units # It must be done after blockages to ensure no DRCs between expanded pins and blocked grids - #start_time = datetime.now() for pin in pin_list: self.convert_pins(pin) - #print_time("Convert pins",datetime.now(), start_time) - #start_time = datetime.now() - for pin in pin_list: - self.combine_adjacent_pins(pin) - #print_time("Combine pins",datetime.now(), start_time) - #self.write_debug_gds("debug_combine_pins.gds",stop_program=True) + #for pin in pin_list: + # self.combine_adjacent_pins(pin) - # Separate any adjacent grids of differing net names to prevent wide metal DRC violations + # Separate any adjacent grids of differing net names that overlap # Must be done before enclosing pins - #start_time = datetime.now() - self.separate_adjacent_pins(self.supply_rail_space_width) - #print_time("Separate pins",datetime.now(), start_time) - # For debug - #self.separate_adjacent_pins(1) + self.separate_adjacent_pins(0) # Enclose the continguous grid units in a metal rectangle to fix some DRCs - #start_time = datetime.now() self.enclose_pins() - #print_time("Enclose pins",datetime.now(), start_time) - #self.write_debug_gds("debug_enclose_pins.gds",stop_program=True) def combine_adjacent_pins(self, pin_name): """ Find pins that have adjacent routing tracks and merge them into a single pin_group. The pins themselves may not be touching, but - enclose_pis in the next step will ensure they are touching. + enclose_pins in the next step will ensure they are touching. """ debug.info(1,"Combining adjacent pins for {}.".format(pin_name)) # Find all adjacencies @@ -257,18 +236,19 @@ class router(router_tech): This will try to separate all grid pins by the supplied number of separation tracks (default is to prevent adjacency). """ - debug.info(1,"Separating adjacent pins.") # Commented out to debug with SCMOS #if separation==0: # return - - pin_names = self.pin_groups.keys() - for pin_name1 in pin_names: - for pin_name2 in pin_names: - if pin_name1==pin_name2: - continue - self.separate_adjacent_pin(pin_name1, pin_name2, separation) + pin_names = self.pin_groups.keys() + for i,pin_name1 in enumerate(pin_names): + for j,pin_name2 in enumerate(pin_names): + if i==j: + continue + if i>j: + return + self.separate_adjacent_pin(pin_name1, pin_name2, separation) + def separate_adjacent_pin(self, pin_name1, pin_name2, separation): """ Go through all of the pin groups and check if any other pin group is @@ -277,50 +257,57 @@ class router(router_tech): Try to do this intelligently to keep th pins enclosed. """ debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2)) + removed_grids = 0 for index1,pg1 in enumerate(self.pin_groups[pin_name1]): for index2,pg2 in enumerate(self.pin_groups[pin_name2]): - # FIgXME: Use separation distance and edge grids only - grids_g1, grids_g2 = pg1.adjacent_grids(pg2, separation) + adj_grids = pg1.adjacent_grids(pg2, separation) + removed_grids += len(adj_grids) # These should have the same length, so... - if len(grids_g1)>0: - debug.info(3,"Adjacent grids {0} {1} {2} {3}".format(index1,grids_g1,index2,grids_g2)) - self.remove_adjacent_grid(pg1, grids_g1, pg2, grids_g2) + if len(adj_grids)>0: + debug.info(3,"Adjacent grids {0} {1} adj={2}".format(index1,index2,adj_grids)) + self.remove_adjacent_grid(pg1, pg2, adj_grids) + - def remove_adjacent_grid(self, pg1, grids1, pg2, grids2): + debug.info(1,"Removed {} adjacent grids.".format(removed_grids)) + + def remove_adjacent_grid(self, pg1, pg2, adj_grids): """ Remove one of the adjacent grids in a heuristic manner. + This will try to keep the groups similar sized by removing from the bigger group. """ - # Determine the bigger and smaller group + if pg1.size()>pg2.size(): bigger = pg1 - bigger_grids = grids1 smaller = pg2 - smaller_grids = grids2 else: bigger = pg2 - bigger_grids = grids2 smaller = pg1 - smaller_grids = grids1 - # First, see if we can remove grids that are in the secondary grids - # i.e. they aren't necessary to the pin grids - if bigger_grids.issubset(bigger.secondary_grids): - debug.info(3,"Removing {} from bigger {}".format(str(bigger_grids), bigger)) - bigger.grids.difference_update(bigger_grids) - self.blocked_grids.update(bigger_grids) - return - elif smaller_grids.issubset(smaller.secondary_grids): - debug.info(3,"Removing {} from smaller {}".format(str(smaller_grids), smaller)) - smaller.grids.difference_update(smaller_grids) - self.blocked_grids.update(smaller_grids) - return + for adj in adj_grids: - # If that fails, just randomly remove from the bigger one and give a warning. - # This might fail later. - debug.warning("Removing arbitrary grids from a pin group {} {}".format(bigger, bigger_grids)) - debug.check(len(bigger.grids)>len(bigger_grids),"Zero size pin group after adjacency removal {} {}".format(bigger, bigger_grids)) - bigger.grids.difference_update(bigger_grids) - self.blocked_grids.update(bigger_grids) + + # If the adjacent grids are a subset of the secondary grids (i.e. not necessary) + # remove them from each + if adj in bigger.secondary_grids: + debug.info(3,"Removing {} from bigger secondary {}".format(adj, bigger)) + bigger.grids.remove(adj) + bigger.secondary_grids.remove(adj) + self.blocked_grids.add(adj) + elif adj in smaller.secondary_grids: + debug.info(3,"Removing {} from smaller secondary {}".format(adj, smaller)) + smaller.grids.remove(adj) + smaller.secondary_grids.remove(adj) + self.blocked_grids.add(adj) + else: + # If we couldn't remove from a secondary grid, we must remove from the primary + # grid of at least one pin + if adj in bigger.grids: + debug.info(3,"Removing {} from bigger primary {}".format(adj, bigger)) + bigger.grids.remove(adj) + elif adj in smaller.grids: + debug.info(3,"Removing {} from smaller primary {}".format(adj, smaller)) + smaller.grids.remove(adj) + @@ -359,17 +346,6 @@ class router(router_tech): self.set_blockages(blockage_grids,False) - # def translate_coordinates(self, coord, mirr, angle, xyShift): - # """ - # Calculate coordinates after flip, rotate, and shift - # """ - # coordinate = [] - # for item in coord: - # x = (item[0]*math.cos(angle)-item[1]*mirr*math.sin(angle)+xyShift[0]) - # y = (item[0]*math.sin(angle)+item[1]*mirr*math.cos(angle)+xyShift[1]) - # coordinate += [(x, y)] - # return coordinate - def convert_shape_to_units(self, shape): """ Scale a shape (two vector list) to user units @@ -496,11 +472,6 @@ class router(router_tech): # and the track points are at the center ll = ll.round() ur = ur.round() - # if ll[0]<45 and ll[0]>35 and ll[1]<5 and ll[1]>-5: - # debug.info(0,"Converting [ {0} , {1} ]".format(old_ll,old_ur)) - # debug.info(0,"Converted [ {0} , {1} ]".format(ll,ur)) - # pin=self.convert_track_to_shape(ll) - # debug.info(0,"Pin {}".format(pin)) return [ll,ur] def convert_pin_to_tracks(self, pin_name, pin, expansion=0): @@ -515,36 +486,24 @@ class router(router_tech): # scale the size bigger to include neaby tracks ll=ll.scale(self.track_factor).floor() ur=ur.scale(self.track_factor).ceil() - #print(pin) + # Keep tabs on tracks with sufficient and insufficient overlap sufficient_list = set() insufficient_list = set() - + zindex=self.get_zindex(pin.layer_num) for x in range(int(ll[0])+expansion,int(ur[0])+1+expansion): for y in range(int(ll[1]+expansion),int(ur[1])+1+expansion): - (full_overlap,partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex)) + (full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex)) if full_overlap: sufficient_list.update([full_overlap]) if partial_overlap: insufficient_list.update([partial_overlap]) - debug.info(4,"Converting [ {0} , {1} ] full={2} partial={3}".format(x,y, full_overlap, partial_overlap)) + debug.info(2,"Converting [ {0} , {1} ] full={2}".format(x,y, full_overlap)) - # Remove the blocked grids - sufficient_list.difference_update(self.blocked_grids) - insufficient_list.difference_update(self.blocked_grids) - - if len(sufficient_list)>0: - return sufficient_list - elif expansion==0 and len(insufficient_list)>0: - best_pin = self.get_all_offgrid_pin(pin, insufficient_list) - #print(best_pin) - return best_pin - elif expansion>0: - nearest_pin = self.get_furthest_offgrid_pin(pin, insufficient_list) - return nearest_pin - else: - return set() + # Return all grids with any potential overlap (sufficient or not) + return (sufficient_list,insufficient_list) + def get_all_offgrid_pin(self, pin, insufficient_list): """ @@ -568,7 +527,6 @@ class router(router_tech): """ Find a list of the single pin with the most overlap. """ - #print("INSUFFICIENT LIST",insufficient_list) # Find the coordinate with the most overlap best_coord = None best_overlap = -math.inf @@ -589,7 +547,6 @@ class router(router_tech): Get a grid cell that is the furthest from the blocked grids. """ - #print("INSUFFICIENT LIST",insufficient_list) # Find the coordinate with the most overlap best_coord = None best_dist = math.inf @@ -606,7 +563,6 @@ class router(router_tech): Given a pin and a list of grid cells (probably non-overlapping), return the nearest grid cell (center to center). """ - #print("INSUFFICIENT LIST",insufficient_list) # Find the coordinate with the most overlap best_coord = None best_dist = math.inf @@ -622,30 +578,32 @@ class router(router_tech): def convert_pin_coord_to_tracks(self, pin, coord): """ - Given a pin and a track coordinate, determine if the pin overlaps enough. - If it does, add additional metal to make the pin "on grid". - If it doesn't, add it to the blocked grid list. + Return all tracks that an inflated pin overlaps """ - (width, spacing) = self.get_layer_width_space(coord.z) - - # This is the rectangle if we put a pin in the center of the track - track_pin = self.convert_track_to_pin(coord) + # This is using the full track shape rather than a single track pin shape + # because we will later patch a connector if there isn't overlap. + track_pin = self.convert_track_to_shape_pin(coord) + + # This is the normal pin inflated by a minimum design rule + inflated_pin = pin_layout(pin.name, pin.inflate(0.5*self.track_space), pin.layer) + overlap_length = pin.overlap_length(track_pin) - - debug.info(3,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length)) - # If it overlaps by more than the min width DRC, we can just use the track - if overlap_length==math.inf or snap_val_to_grid(overlap_length) >= snap_val_to_grid(width): - debug.info(3," Overlap: {0} >? {1}".format(overlap_length,spacing)) - return (coord, None) - # Otherwise, keep track of the partial overlap grids in case we need to patch it later. + debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length)) + inflated_overlap_length = inflated_pin.overlap_length(track_pin) + debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, inflated_pin.rect, track_pin, inflated_overlap_length)) + + # If it overlaps with the pin, it is sufficient + if overlap_length==math.inf or overlap_length > 0: + debug.info(2," Overlap: {0} >? {1}".format(overlap_length,0)) + return (coord,None) + # If it overlaps with the inflated pin, it is partial + elif inflated_overlap_length==math.inf or inflated_overlap_length > 0: + debug.info(2," Partial overlap: {0} >? {1}".format(inflated_overlap_length,0)) + return (None,coord) else: - debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_length,spacing)) - return (None, coord) - - - - + debug.info(2," No overlap: {0} {1}".format(overlap_length,0)) + return (None,None) def convert_track_to_pin(self, track): @@ -653,25 +611,34 @@ class router(router_tech): Convert a grid point into a rectangle shape that is centered track in the track and leaves half a DRC space in each direction. """ - # space depends on which layer it is - if self.get_layer(track[2])==self.horiz_layer_name: - space = 0.5*self.horiz_layer_spacing - else: - space = 0.5*self.vert_layer_spacing - # calculate lower left - x = track.x*self.track_width - 0.5*self.track_width + space - y = track.y*self.track_width - 0.5*self.track_width + space + x = track.x*self.track_width - 0.5*self.track_width + 0.5*self.track_space + y = track.y*self.track_width - 0.5*self.track_width + 0.5*self.track_space ll = snap_to_grid(vector(x,y)) # calculate upper right - x = track.x*self.track_width + 0.5*self.track_width - space - y = track.y*self.track_width + 0.5*self.track_width - space + x = track.x*self.track_width + 0.5*self.track_width - 0.5*self.track_space + y = track.y*self.track_width + 0.5*self.track_width - 0.5*self.track_space ur = snap_to_grid(vector(x,y)) p = pin_layout("", [ll, ur], self.get_layer(track[2])) return p + def convert_track_to_shape_pin(self, track): + """ + Convert a grid point into a rectangle shape that occupies the entire centered + track. + """ + # to scale coordinates to tracks + x = track[0]*self.track_width - 0.5*self.track_width + y = track[1]*self.track_width - 0.5*self.track_width + # offset lowest corner object to to (-track halo,-track halo) + ll = snap_to_grid(vector(x,y)) + ur = snap_to_grid(ll + vector(self.track_width,self.track_width)) + + p = pin_layout("", [ll, ur], self.get_layer(track[2])) + return p + def convert_track_to_shape(self, track): """ Convert a grid point into a rectangle shape that occupies the entire centered @@ -686,6 +653,23 @@ class router(router_tech): return [ll,ur] + def convert_track_to_inflated_pin(self, track): + """ + Convert a grid point into a rectangle shape that is inflated by a half DRC space. + """ + # calculate lower left + x = track.x*self.track_width - 0.5*self.track_width - 0.5*self.track_space + y = track.y*self.track_width - 0.5*self.track_width - 0.5*self.track_space + ll = snap_to_grid(vector(x,y)) + + # calculate upper right + x = track.x*self.track_width + 0.5*self.track_width + 0.5*self.track_space + y = track.y*self.track_width + 0.5*self.track_width + 0.5*self.track_space + ur = snap_to_grid(vector(x,y)) + + p = pin_layout("", [ll, ur], self.get_layer(track[2])) + return p + def analyze_pins(self, pin_name): """ Analyze the shapes of a pin and combine them into groups which are connected. @@ -742,8 +726,6 @@ class router(router_tech): pg.enclose_pin() pg.add_enclosure(self.cell) - #self.write_debug_gds("pin_debug.gds", False) - def add_source(self, pin_name): """ This will mark the grids for all pin components as a source. @@ -813,9 +795,6 @@ class router(router_tech): """ debug.info(4,"Set path: " + str(path)) - # Keep track of path for future blockages - #path.set_blocked() - # This is marked for debug path.set_path() @@ -877,8 +856,6 @@ class router(router_tech): Enclose the tracks from ll to ur in a single rectangle that meets the track DRC rules. """ - # Get the layer information - (width, space) = self.get_layer_width_space(zindex) layer = self.get_layer(zindex) # This finds the pin shape enclosed by the track with DRC spacing on the sides @@ -886,44 +863,10 @@ class router(router_tech): (abs_ll,unused) = pin.rect pin = self.convert_track_to_pin(ur) (unused,abs_ur) = pin.rect - #print("enclose ll={0} ur={1}".format(ll,ur)) - #print("enclose ll={0} ur={1}".format(abs_ll,abs_ur)) pin = pin_layout(name, [abs_ll, abs_ur], layer) return pin - - - def compute_wide_enclosure(self, ll, ur, zindex, name=""): - """ - Enclose the tracks from ll to ur in a single rectangle that meets the track DRC rules. - """ - - # Find the pin enclosure of the whole track shape (ignoring DRCs) - (abs_ll,unused) = self.convert_track_to_shape(ll) - (unused,abs_ur) = self.convert_track_to_shape(ur) - - # Get the layer information - x_distance = abs(abs_ll.x-abs_ur.x) - y_distance = abs(abs_ll.y-abs_ur.y) - shape_width = min(x_distance, y_distance) - shape_length = max(x_distance, y_distance) - - # Get the DRC rule for the grid dimensions - (width, space) = self.get_layer_width_space(zindex, shape_width, shape_length) - layer = self.get_layer(zindex) - - if zindex==0: - spacing = vector(0.5*self.track_width, 0.5*space) - else: - spacing = vector(0.5*space, 0.5*self.track_width) - # Compute the shape offsets with correct spacing - new_ll = abs_ll + spacing - new_ur = abs_ur - spacing - pin = pin_layout(name, [new_ll, new_ur], layer) - - return pin - def contract_path(self,path): """ @@ -963,8 +906,7 @@ class router(router_tech): self.add_route(path) path_set = grid_utils.flatten_set(path) - inflated_path = grid_utils.inflate_set(path_set,self.supply_rail_space_width) - self.path_blockages.append(inflated_path) + self.path_blockages.append(path_set) else: self.write_debug_gds("failed_route.gds") # clean up so we can try a reroute diff --git a/compiler/router/router_tech.py b/compiler/router/router_tech.py index 94c4268a..02b6894f 100644 --- a/compiler/router/router_tech.py +++ b/compiler/router/router_tech.py @@ -3,48 +3,62 @@ from contact import contact from pin_group import pin_group from vector import vector import debug +import math class router_tech: """ This is a class to hold the router tech constants. """ - def __init__(self, layers): + def __init__(self, layers, rail_track_width): """ Allows us to change the layers that we are routing on. First layer is always horizontal, middle is via, and last is always vertical. """ self.layers = layers + self.rail_track_width = rail_track_width + (self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers - # This is the minimum routed track spacing via_connect = contact(self.layers, (1, 1)) - self.max_via_size = max(via_connect.width,via_connect.height) - - self.vert_layer_minwidth = drc("minwidth_{0}".format(self.vert_layer_name)) - self.vert_layer_spacing = drc(str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name)) + max_via_size = max(via_connect.width,via_connect.height) + + self.horiz_layer_number = layer[self.horiz_layer_name] self.vert_layer_number = layer[self.vert_layer_name] - self.horiz_layer_minwidth = drc("minwidth_{0}".format(self.horiz_layer_name)) - self.horiz_layer_spacing = drc(str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name)) - self.horiz_layer_number = layer[self.horiz_layer_name] - - self.horiz_track_width = self.max_via_size + self.horiz_layer_spacing - self.vert_track_width = self.max_via_size + self.vert_layer_spacing + if self.rail_track_width>1: + (self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1) + (self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0) + # For supplies, we will make the wire wider than the vias + self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size) + self.horiz_layer_minwidth = max(self.horiz_layer_minwidth, max_via_size) + + self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing + self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing + + else: + (self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1) + (self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0) + + self.horiz_track_width = max_via_size + self.horiz_layer_spacing + self.vert_track_width = max_via_size + self.vert_layer_spacing + # We'll keep horizontal and vertical tracks the same for simplicity. self.track_width = max(self.horiz_track_width,self.vert_track_width) - debug.info(1,"Track width: "+str(self.track_width)) - + debug.info(1,"Track width: {:.3f}".format(self.track_width)) + self.track_space = max(self.horiz_layer_spacing,self.vert_layer_spacing) + debug.info(1,"Track space: {:.3f}".format(self.track_space)) + self.track_wire = self.track_width - self.track_space + debug.info(1,"Track wire width: {:.3f}".format(self.track_wire)) + self.track_widths = vector([self.track_width] * 2) self.track_factor = vector([1/self.track_width] * 2) - debug.info(2,"Track factor: {0}".format(self.track_factor)) - - # When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side) - self.layer_widths = [self.track_width - self.horiz_layer_spacing, 1, self.track_width - self.vert_layer_spacing] - - + debug.info(2,"Track factor: {}".format(self.track_factor)) + # When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side) + self.layer_widths = [self.track_wire, 1, self.track_wire] + def get_zindex(self,layer_num): if layer_num==self.horiz_layer_number: return 0 @@ -76,4 +90,24 @@ class router_tech: return (min_width,min_spacing) + + def get_supply_layer_width_space(self, zindex): + """ + These are the width and spacing of a supply layer given a supply rail + of the given number of min wire widths. + """ + if zindex==1: + layer_name = self.vert_layer_name + elif zindex==0: + layer_name = self.horiz_layer_name + else: + debug.error("Invalid zindex for track", -1) + min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf) + + min_width = drc("minwidth_{0}".format(layer_name), self.rail_track_width*min_wire_width, math.inf) + min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.rail_track_width*min_wire_width, math.inf) + + return (min_width,min_spacing) + + diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py index a56b1a42..3106decd 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_router.py @@ -24,17 +24,16 @@ class supply_router(router): This will route on layers in design. It will get the blockages from either the gds file name or the design itself (by saving to a gds file). """ - router.__init__(self, layers, design, gds_filename) + # Power rail width in minimum wire widths + self.rail_track_width = 3 + + router.__init__(self, layers, design, gds_filename, self.rail_track_width) # The list of supply rails (grid sets) that may be routed self.supply_rails = {} - self.supply_rail_wires = {} # This is the same as above but as a sigle set for the all the rails self.supply_rail_tracks = {} - self.supply_rail_wire_tracks = {} - # Power rail width in grid units. - self.rail_track_width = 2 @@ -65,17 +64,10 @@ class supply_router(router): # but this is simplest for now. self.create_routing_grid() - # Compute the grid dimensions - self.compute_supply_rail_dimensions() - # Get the pin shapes - #start_time = datetime.now() self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) - #print_time("Pins and blockages",datetime.now(), start_time) - #self.write_debug_gds("pin_enclosures.gds",stop_program=True) # Add the supply rails in a mesh network and connect H/V with vias - #start_time = datetime.now() # Block everything self.prepare_blockages(self.gnd_name) # Determine the rail locations @@ -85,23 +77,15 @@ class supply_router(router): self.prepare_blockages(self.vdd_name) # Determine the rail locations self.route_supply_rails(self.vdd_name,1) - #self.write_debug_gds("debug_rails.gds",stop_program=True) - #print_time("Supply rails",datetime.now(), start_time) - #start_time = datetime.now() self.route_simple_overlaps(vdd_name) self.route_simple_overlaps(gnd_name) - #print_time("Simple overlaps",datetime.now(), start_time) - #self.write_debug_gds("debug_simple_route.gds",stop_program=False) # Route the supply pins to the supply rails # Route vdd first since we want it to be shorter - #start_time = datetime.now() self.route_pins_to_rails(vdd_name) self.route_pins_to_rails(gnd_name) - #print_time("Routing",datetime.now(), start_time) - #self.write_debug_gds("debug_pin_routes.gds",stop_program=True) - + #self.write_debug_gds("final.gds",False) return True @@ -116,10 +100,8 @@ class supply_router(router): debug.info(1,"Routing simple overlap pins for {0}".format(pin_name)) # These are the wire tracks - wire_tracks = self.supply_rail_wire_tracks[pin_name] - # These are the wire and space tracks - supply_tracks = self.supply_rail_tracks[pin_name] - + wire_tracks = self.supply_rail_tracks[pin_name] + routed_count=0 for pg in self.pin_groups[pin_name]: if pg.is_routed(): continue @@ -127,17 +109,15 @@ class supply_router(router): # First, check if we just overlap, if so, we are done. overlap_grids = wire_tracks & pg.grids if len(overlap_grids)>0: + routed_count += 1 pg.set_routed() continue - - # Else, if we overlap some of the space track, we can patch it with an enclosure - common_set = supply_tracks & pg.grids - if len(common_set)>0: - pg.create_simple_overlap_enclosure(common_set) - pg.add_enclosure(self.cell) + # Else, if we overlap some of the space track, we can patch it with an enclosure + #pg.create_simple_overlap_enclosure(pg.grids) + #pg.add_enclosure(self.cell) - + debug.info(1,"Routed {} simple overlap pins".format(routed_count)) def finalize_supply_rails(self, name): """ @@ -146,7 +126,7 @@ class supply_router(router): NOTE: It is still possible though unlikely that there are disconnected groups of rails. """ - all_rails = self.supply_rail_wires[name] + all_rails = self.supply_rails[name] connections = set() via_areas = [] @@ -186,8 +166,8 @@ class supply_router(router): # the indices to determine a rail is connected to another # the overlap area for placement of a via overlap = new_r1 & new_r2 - if len(overlap) >= self.supply_rail_wire_width**2: - debug.info(3,"Via overlap {0} {1} {2}".format(len(overlap),self.supply_rail_wire_width**2,overlap)) + if len(overlap) >= 1: + debug.info(3,"Via overlap {0} {1}".format(len(overlap),overlap)) connections.update([i1,i2]) via_areas.append(overlap) @@ -196,7 +176,7 @@ class supply_router(router): ll = grid_utils.get_lower_left(area) ur = grid_utils.get_upper_right(area) center = (ll + ur).scale(0.5,0.5,0) - self.add_via(center,self.rail_track_width) + self.add_via(center,1) # Determien which indices were not connected to anything above missing_indices = set([x for x in range(len(self.supply_rails[name]))]) @@ -209,7 +189,6 @@ class supply_router(router): ur = grid_utils.get_upper_right(all_rails[rail_index]) debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur)) self.supply_rails[name].pop(rail_index) - self.supply_rail_wires[name].pop(rail_index) # Make the supply rails into a big giant set of grids for easy blockages. # Must be done after we determine which ones are connected. @@ -226,7 +205,7 @@ class supply_router(router): ll = grid_utils.get_lower_left(rail) ur = grid_utils.get_upper_right(rail) z = ll.z - pin = self.compute_wide_enclosure(ll, ur, z, name) + pin = self.compute_pin_enclosure(ll, ur, z, name) debug.info(2,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin)) self.cell.add_layout_pin(text=name, layer=pin.layer, @@ -234,43 +213,6 @@ class supply_router(router): width=pin.width(), height=pin.height()) - def compute_supply_rail_dimensions(self): - """ - Compute the supply rail dimensions including wide metal spacing rules. - """ - - self.max_yoffset = self.rg.ur.y - self.max_xoffset = self.rg.ur.x - - # Longest length is conservative - rail_length = max(self.max_yoffset,self.max_xoffset) - # Convert the number of tracks to dimensions to get the design rule spacing - rail_width = self.track_width*self.rail_track_width - - # Get the conservative width and spacing of the top rails - (horizontal_width, horizontal_space) = self.get_layer_width_space(0, rail_width, rail_length) - (vertical_width, vertical_space) = self.get_layer_width_space(1, rail_width, rail_length) - width = max(horizontal_width, vertical_width) - space = max(horizontal_space, vertical_space) - - # This is the supply rail pitch in terms of routing grids - # i.e. a rail of self.rail_track_width needs this many tracks including - # space - track_pitch = self.rail_track_width*width + space - - # Determine the pitch (in tracks) of the rail wire + spacing - self.supply_rail_width = math.ceil(track_pitch/self.track_width) - debug.info(1,"Rail step: {}".format(self.supply_rail_width)) - - # Conservatively determine the number of tracks that the rail actually occupies - space_tracks = math.ceil(space/self.track_width) - self.supply_rail_wire_width = self.supply_rail_width - space_tracks - debug.info(1,"Rail wire tracks: {}".format(self.supply_rail_wire_width)) - total_space = self.supply_rail_width - self.supply_rail_wire_width - self.supply_rail_space_width = math.floor(0.5*total_space) - debug.info(1,"Rail space tracks: {} (on both sides)".format(self.supply_rail_space_width)) - - def compute_supply_rails(self, name, supply_number): """ Compute the unblocked locations for the horizontal and vertical supply rails. @@ -279,16 +221,20 @@ class supply_router(router): """ self.supply_rails[name]=[] - self.supply_rail_wires[name]=[] - start_offset = supply_number*self.supply_rail_width + max_yoffset = self.rg.ur.y + max_xoffset = self.rg.ur.x + min_yoffset = self.rg.ll.y + min_xoffset = self.rg.ll.x + # Horizontal supply rails - for offset in range(start_offset, self.max_yoffset, 2*self.supply_rail_width): + start_offset = min_yoffset + supply_number + for offset in range(start_offset, max_yoffset, 2): # Seed the function at the location with the given width - wave = [vector3d(0,offset+i,0) for i in range(self.supply_rail_width)] + wave = [vector3d(min_xoffset,offset,0)] # While we can keep expanding east in this horizontal track - while wave and wave[0].x < self.max_xoffset: + while wave and wave[0].x < max_xoffset: added_rail = self.find_supply_rail(name, wave, direction.EAST) if not added_rail: # Just seed with the next one @@ -298,12 +244,12 @@ class supply_router(router): wave = added_rail.neighbor(direction.EAST) # Vertical supply rails - max_offset = self.rg.ur.x - for offset in range(start_offset, self.max_xoffset, 2*self.supply_rail_width): + start_offset = min_xoffset + supply_number + for offset in range(start_offset, max_xoffset, 2): # Seed the function at the location with the given width - wave = [vector3d(offset+i,0,1) for i in range(self.supply_rail_width)] + wave = [vector3d(offset,min_yoffset,1)] # While we can keep expanding north in this vertical track - while wave and wave[0].y < self.max_yoffset: + while wave and wave[0].y < max_yoffset: added_rail = self.find_supply_rail(name, wave, direction.NORTH) if not added_rail: # Just seed with the next one @@ -378,11 +324,6 @@ class supply_router(router): if len(wave_path)>=4*self.rail_track_width: grid_set = wave_path.get_grids() self.supply_rails[name].append(grid_set) - - start_wire_index = self.supply_rail_space_width - end_wire_index = self.supply_rail_width - self.supply_rail_space_width - wire_set = wave_path.get_wire_grids(start_wire_index,end_wire_index) - self.supply_rail_wires[name].append(wire_set) return True return False @@ -417,10 +358,6 @@ class supply_router(router): rail_set.update(rail) self.supply_rail_tracks[pin_name] = rail_set - wire_set = set() - for rail in self.supply_rail_wires[pin_name]: - wire_set.update(rail) - self.supply_rail_wire_tracks[pin_name] = wire_set def route_pins_to_rails(self, pin_name): @@ -430,8 +367,8 @@ class supply_router(router): """ remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name]) - debug.info(1,"Routing {0} with {1} pin components to route.".format(pin_name, - remaining_components)) + debug.info(1,"Maze routing {0} with {1} pin components to connect.".format(pin_name, + remaining_components)) for index,pg in enumerate(self.pin_groups[pin_name]): if pg.is_routed(): @@ -465,7 +402,7 @@ class supply_router(router): """ debug.info(4,"Add supply rail target {}".format(pin_name)) # Add the wire itself as the target - self.rg.set_target(self.supply_rail_wire_tracks[pin_name]) + self.rg.set_target(self.supply_rail_tracks[pin_name]) # But unblock all the rail tracks including the space self.rg.set_blocked(self.supply_rail_tracks[pin_name],False) diff --git a/compiler/router/tests/10_supply_grid_test.py b/compiler/router/tests/10_supply_grid_test.py index ef9a1be3..7258ab40 100755 --- a/compiler/router/tests/10_supply_grid_test.py +++ b/compiler/router/tests/10_supply_grid_test.py @@ -22,6 +22,9 @@ class no_blockages_test(openram_test): if False: from control_logic import control_logic cell = control_logic(16) + layer_stack =("metal3","via3","metal4") + rtr=router(layer_stack, cell) + self.assertTrue(rtr.route()) else: from sram import sram from sram_config import sram_config @@ -33,9 +36,6 @@ class no_blockages_test(openram_test): sram = sram(c, "sram1") cell = sram.s - layer_stack =("metal3","via3","metal4") - rtr=router(layer_stack, cell) - self.assertTrue(rtr.route()) self.local_check(cell,True) # fails if there are any DRC errors on any cells diff --git a/compiler/sram.py b/compiler/sram.py index 1b6b104f..618572ab 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -14,7 +14,6 @@ class sram(): """ def __init__(self, sram_config, name): - sram_config.compute_sizes() sram_config.set_local_config(self) # reset the static duplicate name checker for unit tests @@ -34,8 +33,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..6b2caf30 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -10,6 +10,7 @@ from globals import OPTS, print_time from sram_base import sram_base from bank import bank +from contact import m2m3 from dff_buf_array import dff_buf_array from dff_array import dff_array @@ -21,11 +22,6 @@ class sram_1bank(sram_base): def __init__(self, name, sram_config): sram_base.__init__(self, name, sram_config) - - def create_netlist(self): - sram_base.create_netlist(self) - self.create_modules() - def create_modules(self): """ This adds the modules for a single bank SRAM with control @@ -61,15 +57,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 +80,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 +91,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 +114,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 +125,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 +179,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 +195,40 @@ 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) - # 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]) + + # 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 above + self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, clk_steiner_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) + # In some designs, the steiner via will be too close to the mid_pos via + # so make the wire as wide as the contacts + self.add_path("metal2",[mid_pos, clk_steiner_pos], width=max(m2m3.width,m2m3.height)) + 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 +276,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..d27cbd1b 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -2,6 +2,7 @@ import sys import datetime import getpass import debug +from datetime import datetime from importlib import reload from vector import vector from globals import OPTS, print_time @@ -63,37 +64,52 @@ class sram_base(design): def create_netlist(self): """ Netlist creation """ - + + start_time = datetime.now() + # Must create the control logic before pins to get the pins self.add_modules() self.add_pins() + self.create_modules() # This is for the lib file if we don't create layout self.width=0 self.height=0 + + + if not OPTS.is_unit_test: + print_time("Submodules",datetime.now(), start_time) def create_layout(self): """ Layout creation """ + start_time = datetime.now() self.place_instances() + if not OPTS.is_unit_test: + print_time("Placement",datetime.now(), start_time) + start_time = datetime.now() self.route_layout() + self.route_supplies() + if not OPTS.is_unit_test: + print_time("Routing",datetime.now(), start_time) self.add_lvs_correspondence_points() self.offset_all_coordinates() - # Must be done after offsetting lower-left - self.route_supplies() - highest_coord = self.find_highest_coords() self.width = highest_coord[0] self.height = highest_coord[1] - + start_time = datetime.now() self.DRC_LVS(final_verification=True) + if not OPTS.is_unit_test: + print_time("Verification",datetime.now(), start_time) - + def create_modules(self): + debug.error("Must override pure virtual function.",-1) + def route_supplies(self): """ Route the supply grid and connect the pins to them. """ @@ -140,11 +156,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 +308,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 +425,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/sram_config.py b/compiler/sram_config.py index 3c3892a5..e4f94b4a 100644 --- a/compiler/sram_config.py +++ b/compiler/sram_config.py @@ -14,7 +14,7 @@ class sram_config: # This will get over-written when we determine the organization self.words_per_row = None - # Move the module names to this? + self.compute_sizes() def set_local_config(self, module): @@ -54,6 +54,20 @@ class sram_config: self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size) self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row) + debug.info(1,"Words per row: {}".format(self.words_per_row)) + self.recompute_sizes() + + def recompute_sizes(self): + """ + Calculate the auxiliary values assuming fixed number of words per row. + This can be called multiple times from the unit test when we reconfigure an + SRAM for testing. + """ + + # If the banks changed + self.num_words_per_bank = self.num_words/self.num_banks + self.num_bits_per_bank = self.word_size*self.num_words_per_bank + # Fix the number of columns and rows self.num_cols = int(self.words_per_row*self.word_size) self.num_rows = int(self.num_words_per_bank/self.words_per_row) @@ -64,7 +78,6 @@ class sram_config: self.bank_addr_size = self.col_addr_size + self.row_addr_size self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) - debug.info(1,"Words per row: {}".format(self.words_per_row)) def estimate_words_per_row(self,tentative_num_cols, word_size): """ @@ -74,10 +87,14 @@ class sram_config: if tentative_num_cols < 1.5*word_size: return 1 - elif tentative_num_cols > 3*word_size: + elif tentative_num_cols < 3*word_size: + return 2 + elif tentative_num_cols < 6*word_size: return 4 else: - return 2 + if tentative_num_cols > 16*word_size: + debug.warning("Reaching column mux size limit. Consider increasing above 8-way.") + return 8 def amend_words_per_row(self,tentative_num_rows, words_per_row): """ 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/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py index 9bf32423..0eff040d 100755 --- a/compiler/tests/19_multi_bank_test.py +++ b/compiler/tests/19_multi_bank_test.py @@ -24,18 +24,21 @@ class multi_bank_test(openram_test): c.num_banks=2 c.words_per_row=1 + c.recompute_sizes() debug.info(1, "No column mux") a = bank(c, name="bank1_multi") self.local_check(a) c.num_words=32 c.words_per_row=2 + c.recompute_sizes() debug.info(1, "Two way column mux") a = bank(c, name="bank2_multi") self.local_check(a) c.num_words=64 c.words_per_row=4 + c.recompute_sizes() debug.info(1, "Four way column mux") a = bank(c, name="bank3_multi") self.local_check(a) @@ -43,6 +46,7 @@ class multi_bank_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 + c.recompute_sizes() debug.info(1, "Eight way column mux") a = bank(c, name="bank4_multi") self.local_check(a) diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py index 6496c16f..ff19ac15 100755 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -31,6 +31,7 @@ class psingle_bank_test(openram_test): num_words=16) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "No column mux") name = "bank1_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) a = bank(c, name=name) @@ -38,6 +39,7 @@ class psingle_bank_test(openram_test): c.num_words=32 c.words_per_row=2 + c.recompute_sizes() debug.info(1, "Two way column mux") name = "bank2_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) a = bank(c, name=name) @@ -45,6 +47,7 @@ class psingle_bank_test(openram_test): c.num_words=64 c.words_per_row=4 + c.recompute_sizes() debug.info(1, "Four way column mux") name = "bank3_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) a = bank(c, name=name) @@ -53,6 +56,7 @@ class psingle_bank_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 + c.recompute_sizes() debug.info(1, "Four way column mux") name = "bank4_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) a = bank(c, name=name) 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..f2f6386c --- /dev/null +++ b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py @@ -0,0 +1,51 @@ +#!/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 + c.recompute_sizes() + 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..3d049aef 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,14 @@ 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") + c.recompute_sizes() + 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..3afd2c9b 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,14 @@ 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") + c.recompute_sizes() + 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..1be26ca7 --- /dev/null +++ b/compiler/tests/20_psram_1bank_4mux_1rw_1r_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 + +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 + c.recompute_sizes() + 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..ea5fba78 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,14 @@ 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") + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram 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..26a7755f 100755 --- a/compiler/tests/20_sram_1bank_2mux_test.py +++ b/compiler/tests/20_sram_1bank_2mux_test.py @@ -23,7 +23,14 @@ 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") + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram 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..16654be5 100755 --- a/compiler/tests/20_sram_1bank_4mux_test.py +++ b/compiler/tests/20_sram_1bank_4mux_test.py @@ -23,7 +23,14 @@ 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") + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram 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..dfd8a6a1 --- /dev/null +++ b/compiler/tests/20_sram_1bank_8mux_1rw_1r_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 + +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 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram 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..dde1a448 100755 --- a/compiler/tests/20_sram_1bank_8mux_test.py +++ b/compiler/tests/20_sram_1bank_8mux_test.py @@ -23,7 +23,14 @@ 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") + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram 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..02e82687 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,14 @@ 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") + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram 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..7a03ce1e 100755 --- a/compiler/tests/20_sram_1bank_nomux_test.py +++ b/compiler/tests/20_sram_1bank_nomux_test.py @@ -23,7 +23,14 @@ 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") + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram 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_2bank_test.py b/compiler/tests/20_sram_2bank_test.py index ab8c6ec2..59db981d 100755 --- a/compiler/tests/20_sram_2bank_test.py +++ b/compiler/tests/20_sram_2bank_test.py @@ -23,18 +23,21 @@ class sram_2bank_test(openram_test): num_banks=2) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Two bank, no column mux with control logic") a = sram(c, "sram1") self.local_check(a, final_verification=True) c.num_words=64 c.words_per_row=2 + c.recompute_sizes() debug.info(1, "Two bank two way column mux with control logic") a = sram(c, "sram2") self.local_check(a, final_verification=True) c.num_words=128 c.words_per_row=4 + c.recompute_sizes() debug.info(1, "Two bank, four way column mux with control logic") a = sram(c, "sram3") self.local_check(a, final_verification=True) @@ -42,6 +45,7 @@ class sram_2bank_test(openram_test): c.word_size=2 c.num_words=256 c.words_per_row=8 + c.recompute_sizes() debug.info(1, "Two bank, eight way column mux with control logic") a = sram(c, "sram4") 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..c01df3fa 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -30,6 +30,7 @@ class timing_sram_test(openram_test): num_words=16, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") s = sram(c, name="sram1") @@ -49,29 +50,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..a4a758a5 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -30,6 +30,7 @@ class timing_sram_test(openram_test): num_words=16, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") s = sram(c, name="sram1") @@ -50,27 +51,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..284b1922 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,14 @@ 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)) + c.recompute_sizes() + 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..64e5a3d5 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,14 @@ 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)) + c.recompute_sizes() + 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..294f6721 100755 --- a/compiler/tests/22_psram_1bank_8mux_func_test.py +++ b/compiler/tests/22_psram_1bank_8mux_func_test.py @@ -35,10 +35,14 @@ 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)) + c.recompute_sizes() + 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..8bf37793 100755 --- a/compiler/tests/22_psram_1bank_nomux_func_test.py +++ b/compiler/tests/22_psram_1bank_nomux_func_test.py @@ -35,10 +35,14 @@ 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)) + c.recompute_sizes() + 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_sram_1bank_2mux_func_test.py b/compiler/tests/22_sram_1bank_2mux_func_test.py index 8b195c95..ef36b638 100755 --- a/compiler/tests/22_sram_1bank_2mux_func_test.py +++ b/compiler/tests/22_sram_1bank_2mux_func_test.py @@ -30,6 +30,7 @@ class sram_1bank_2mux_func_test(openram_test): num_words=64, num_banks=1) c.words_per_row=2 + c.recompute_sizes() debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, c.num_words, c.words_per_row, diff --git a/compiler/tests/22_sram_1bank_4mux_func_test.py b/compiler/tests/22_sram_1bank_4mux_func_test.py index 0df3ff0e..f63f8704 100755 --- a/compiler/tests/22_sram_1bank_4mux_func_test.py +++ b/compiler/tests/22_sram_1bank_4mux_func_test.py @@ -30,6 +30,7 @@ class sram_1bank_4mux_func_test(openram_test): num_words=256, num_banks=1) c.words_per_row=4 + c.recompute_sizes() debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, c.num_words, c.words_per_row, diff --git a/compiler/tests/22_sram_1bank_8mux_func_test.py b/compiler/tests/22_sram_1bank_8mux_func_test.py index 16122a49..02000fb7 100755 --- a/compiler/tests/22_sram_1bank_8mux_func_test.py +++ b/compiler/tests/22_sram_1bank_8mux_func_test.py @@ -33,6 +33,7 @@ class sram_1bank_8mux_func_test(openram_test): num_words=256, num_banks=1) c.words_per_row=8 + c.recompute_sizes() debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, c.num_words, c.words_per_row, diff --git a/compiler/tests/22_sram_1bank_nomux_func_test.py b/compiler/tests/22_sram_1bank_nomux_func_test.py index e6a5bcda..08a21d92 100755 --- a/compiler/tests/22_sram_1bank_nomux_func_test.py +++ b/compiler/tests/22_sram_1bank_nomux_func_test.py @@ -30,6 +30,7 @@ class sram_1bank_nomux_func_test(openram_test): num_words=32, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, c.num_words, c.words_per_row, diff --git a/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py b/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py index aa80656d..21696f93 100755 --- a/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py +++ b/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py @@ -35,6 +35,7 @@ class psram_1bank_nomux_func_test(openram_test): num_words=32, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Functional test for sram 1rw,1r with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, c.num_words, c.words_per_row, diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py index b111a57d..8a996cb4 100755 --- a/compiler/tests/23_lib_sram_model_test.py +++ b/compiler/tests/23_lib_sram_model_test.py @@ -23,6 +23,7 @@ class lib_test(openram_test): num_words=16, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) tempspice = OPTS.openram_temp + "temp.sp" diff --git a/compiler/tests/23_lib_sram_prune_test.py b/compiler/tests/23_lib_sram_prune_test.py index 7f0a9c47..1b93d1fd 100755 --- a/compiler/tests/23_lib_sram_prune_test.py +++ b/compiler/tests/23_lib_sram_prune_test.py @@ -32,6 +32,7 @@ class lib_test(openram_test): num_words=16, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Testing pruned timing for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py index 5534598e..1c26fd45 100755 --- a/compiler/tests/23_lib_sram_test.py +++ b/compiler/tests/23_lib_sram_test.py @@ -32,6 +32,7 @@ class lib_test(openram_test): num_words=16, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) diff --git a/compiler/tests/24_lef_sram_test.py b/compiler/tests/24_lef_sram_test.py index d4bf2619..2d90d12b 100755 --- a/compiler/tests/24_lef_sram_test.py +++ b/compiler/tests/24_lef_sram_test.py @@ -23,6 +23,7 @@ class lef_test(openram_test): num_words=16, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Testing LEF for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py index eebeb258..4aa2fce7 100755 --- a/compiler/tests/25_verilog_sram_test.py +++ b/compiler/tests/25_verilog_sram_test.py @@ -22,6 +22,7 @@ class verilog_test(openram_test): num_words=16, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) diff --git a/compiler/tests/27_worst_case_delay_test.py b/compiler/tests/27_worst_case_delay_test.py index 52dd3422..834c6b69 100755 --- a/compiler/tests/27_worst_case_delay_test.py +++ b/compiler/tests/27_worst_case_delay_test.py @@ -39,7 +39,7 @@ class worst_case_timing_sram_test(openram_test): num_words=num_words, num_banks=num_banks) c.words_per_row=1 - #c.compute_sizes() + c.recompute_sizes() debug.info(1, "Testing the timing different bitecells inside a {}bit, {} words SRAM with {} bank".format( word_size, num_words, num_banks)) s = sram(c, name="sram1") diff --git a/compiler/tests/30_openram_test.py b/compiler/tests/30_openram_test.py index 7450dfba..2561e7a2 100755 --- a/compiler/tests/30_openram_test.py +++ b/compiler/tests/30_openram_test.py @@ -42,13 +42,14 @@ class openram_test(openram_test): OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) - - cmd = "python3 {0}/openram.py -n -o {1} -p {2} {3} config_20_{4}.py 2>&1 > {5}/output.log".format(OPENRAM_HOME, - out_file, - out_path, - verbosity, - OPTS.tech_name, - out_path) + # 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]