diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 98373f3d..2727c178 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -101,7 +101,7 @@ class bank(design.design): for port in range(self.total_ports): self.add_pin("bank_sel{}".format(port),"INPUT") for port in range(self.total_read): - self.add_pin("s_en{0}".format(port), "INPUT") + self.add_pin("s_en{0}".format(self.read_index[port]), "INPUT") for port in range(self.total_write): self.add_pin("w_en{0}".format(port), "INPUT") for port in range(self.total_ports): @@ -399,7 +399,7 @@ class bank(design.design): temp.append(self.read_bl_list[port]+"_out[{0}]".format(bit)) temp.append(self.read_br_list[port]+"_out[{0}]".format(bit)) - temp.extend([self.prefix+"s_en{}".format(port), "vdd", "gnd"]) + temp.extend([self.prefix+"s_en{}".format(self.read_index[port]), "vdd", "gnd"]) self.connect_inst(temp) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index be0a6246..1ae8993e 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -20,8 +20,9 @@ class control_logic(design.design): def __init__(self, num_rows, port="rw"): """ Constructor """ - design.design.__init__(self, "control_logic") - debug.info(1, "Creating {}".format(self.name)) + name = "control_logic_" + port + design.design.__init__(self, name) + debug.info(1, "Creating {}".format(name)) self.num_rows = num_rows self.port = port @@ -95,7 +96,7 @@ class control_logic(design.design): delay_stages = 4 # Must be non-inverting delay_fanout = 3 # This can be anything >=2 bitcell_loads = int(math.ceil(self.num_rows / 5.0)) - self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads) + self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads, name="replica_bitline_"+self.port) self.add_mod(self.replica_bitline) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index 90175743..f02daecb 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -13,8 +13,12 @@ class delay_chain(design.design): Usually, this will be constant, but it could have varied fanout. """ + unique_id = 1 + def __init__(self, fanout_list, name="delay_chain"): """init function""" + name = name+"_{}".format(delay_chain.unique_id) + delay_chain.unique_id += 1 design.design.__init__(self, name) # Two fanouts are needed so that we can route the vdd/gnd connections diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index b9d9cf54..f9107879 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -81,8 +81,8 @@ class replica_bitline(design.design): """ Add the modules for later usage """ from importlib import reload - g = reload(__import__(OPTS.delay_chain)) - self.mod_delay_chain = getattr(g, OPTS.delay_chain) + #g = reload(__import__(OPTS.delay_chain)) + #self.mod_delay_chain = getattr(g, OPTS.delay_chain) g = reload(__import__(OPTS.replica_bitcell)) self.mod_replica_bitcell = getattr(g, OPTS.replica_bitcell) @@ -95,7 +95,8 @@ class replica_bitline(design.design): self.add_mod(self.rbl) # FIXME: The FO and depth of this should be tuned - self.delay_chain = self.mod_delay_chain([self.delay_fanout]*self.delay_stages) + from delay_chain import delay_chain + self.delay_chain = delay_chain([self.delay_fanout]*self.delay_stages) self.add_mod(self.delay_chain) self.inv = pinv() diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index a1ec3677..5356e33b 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -57,60 +57,60 @@ class sram_1bank(sram_base): # the sense amps/column mux and cell array) # The x-coordinate is placed to allow a single clock wire (plus an extra pitch) # up to the row address DFFs. - control_pos = vector(-self.control_logic.width - 2*self.m2_pitch, - self.bank.bank_center.y - self.control_logic.control_logic_center.y) - self.control_logic_inst.place(control_pos) + for port in range(self.total_ports): + control_pos = vector(-self.control_logic.width - 2*self.m2_pitch, + self.bank.bank_center.y - self.control_logic.control_logic_center.y) + self.control_logic_inst[port].place(control_pos) - # The row address bits are placed above the control logic aligned on the right. - row_addr_pos = vector(self.control_logic_inst.rx() - self.row_addr_dff.width, - self.control_logic_inst.uy()) - self.row_addr_dff_inst.place(row_addr_pos) + # The row address bits are placed above the control logic aligned on the right. + row_addr_pos = vector(self.control_logic_inst[0].rx() - self.row_addr_dff.width, + self.control_logic_inst[0].uy()) + self.row_addr_dff_inst[port].place(row_addr_pos) - # 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) - - # Add the column address below the bank under the control - # The column address flops are aligned with the data flops - if self.col_addr_dff: - col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width, - data_gap - self.col_addr_dff.height) - self.col_addr_dff_inst.place(col_addr_pos) - - # Add the data flops below the bank to the right of the center of bank: - # This relies on the center point of the bank: - # decoder in upper left, bank in upper right, sensing in lower right. - # These flops go below the sensing and leave a gap to channel route to the - # sense amps. - data_pos = vector(self.bank.bank_center.x, - data_gap - self.data_dff.height) - self.data_dff_inst.place(data_pos) - - # two supply rails are already included in the bank, so just 2 here. - # self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch - # self.height = self.bank.height + # 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) + + # Add the column address below the bank under the control + # The column address flops are aligned with the data flops + if self.col_addr_dff: + col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width, + data_gap - self.col_addr_dff.height) + self.col_addr_dff_inst[port].place(col_addr_pos) + + # Add the data flops below the bank to the right of the center of bank: + # This relies on the center point of the bank: + # decoder in upper left, bank in upper right, sensing in lower right. + # These flops go below the sensing and leave a gap to channel route to the + # sense amps. + data_pos = vector(self.bank.bank_center.x, + data_gap - self.data_dff.height) + self.data_dff_inst[port].place(data_pos) + + # two supply rails are already included in the bank, so just 2 here. + # self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch + # self.height = self.bank.height def add_layout_pins(self): """ Add the top-level pins for a single bank SRAM with control. """ - # Connect the control pins as inputs - for n in self.control_logic_inputs + ["clk"]: - self.copy_layout_pin(self.control_logic_inst, n) + for port in range(self.total_ports): + # Connect the control pins as inputs + for signal in self.control_logic_inputs[port] + ["clk"]: + self.copy_layout_pin(self.control_logic_inst[port], signal, signal+"{}".format(port)) - for i in range(self.word_size): - dout_name = "dout0[{}]".format(i) - self.copy_layout_pin(self.bank_inst, dout_name, "DOUT0[{}]".format(i)) + for bit in range(self.word_size): + self.copy_layout_pin(self.bank_inst, "dout{0}[{1}]".format(port,bit), "DOUT{0}[{1}]".format(port,bit)) - # Lower address bits - for i in range(self.col_addr_size): - self.copy_layout_pin(self.col_addr_dff_inst, "din[{}]".format(i),"ADDR0[{}]".format(i)) - # Upper address bits - for i in range(self.row_addr_size): - self.copy_layout_pin(self.row_addr_dff_inst, "din[{}]".format(i),"ADDR0[{}]".format(i+self.col_addr_size)) + # Lower address bits + for bit in range(self.col_addr_size): + self.copy_layout_pin(self.col_addr_dff_inst[port], "din[{}]".format(bit),"ADDR{0}[{1}]".format(port,bit)) + # Upper address bits + for bit in range(self.row_addr_size): + self.copy_layout_pin(self.row_addr_dff_inst[port], "din[{}]".format(bit),"ADDR{0}[{1}]".format(port,bit+self.col_addr_size)) - for i in range(self.word_size): - din_name = "din[{}]".format(i) - self.copy_layout_pin(self.data_dff_inst, din_name, "DIN0[{}]".format(i)) + for bit in range(self.word_size): + self.copy_layout_pin(self.data_dff_inst[port], "din[{}]".format(bit), "DIN{0}[{1}]".format(port,bit)) def route(self): """ Route a single bank SRAM """ @@ -134,52 +134,55 @@ class sram_1bank(sram_base): """ Route the clock network """ # This is the actual input to the SRAM - self.copy_layout_pin(self.control_logic_inst, "clk") + for port in range(self.total_ports): + self.copy_layout_pin(self.control_logic_inst[port], "clk", "clk{}".format(port)) - # 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 - - bank_clk_buf_pin = self.bank_inst.get_pin("clk_buf") - bank_clk_buf_pos = bank_clk_buf_pin.center() - bank_clk_buf_bar_pin = self.bank_inst.get_pin("clk_buf_bar") - bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center() + # 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 + + 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_inst.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]) - - data_dff_clk_pin = self.data_dff_inst.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]) + if self.col_addr_dff: + dff_clk_pin = self.col_addr_dff_inst[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]) + + data_dff_clk_pin = self.data_dff_inst[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 of the control/row addr DFF - # to route vertically. - control_clk_buf_pin = self.control_logic_inst.get_pin("clk_buf") - control_clk_buf_pos = control_clk_buf_pin.rc() - row_addr_clk_pin = self.row_addr_dff_inst.get_pin("clk") - row_addr_clk_pos = row_addr_clk_pin.rc() - mid1_pos = vector(self.row_addr_dff_inst.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 uses a metal2 track to the right of the control/row addr DFF + # to route vertically. + control_clk_buf_pin = self.control_logic_inst[port].get_pin("clk_buf") + control_clk_buf_pos = control_clk_buf_pin.rc() + row_addr_clk_pin = self.row_addr_dff_inst[port].get_pin("clk") + row_addr_clk_pos = row_addr_clk_pin.rc() + mid1_pos = vector(self.row_addr_dff_inst[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]) def route_vdd_gnd(self): """ Propagate all vdd/gnd pins up to this level for all modules """ # These are the instances that every bank has - top_instances = [self.bank_inst, - self.row_addr_dff_inst, - self.data_dff_inst, - self.control_logic_inst] - if self.col_addr_dff: - top_instances.append(self.col_addr_dff_inst) + top_instances = [self.bank_inst] + for port in range(self.total_write): + top_instances.append(self.data_dff_inst[port]) + for port in range(self.total_ports): + top_instances.append(self.row_addr_dff_inst[port]) + top_instances.append(self.control_logic_inst[port]) + if self.col_addr_dff: + top_instances.append(self.col_addr_dff_inst[port]) for inst in top_instances: @@ -190,12 +193,14 @@ class sram_1bank(sram_base): """ Propagate all vdd/gnd pins up to this level for all modules """ # These are the instances that every bank has - top_instances = [self.bank_inst, - self.row_addr_dff_inst, - self.data_dff_inst, - self.control_logic_inst] - if self.col_addr_dff: - top_instances.append(self.col_addr_dff_inst) + top_instances = [self.bank_inst] + for port in range(self.total_write): + top_instances.append(self.data_dff_inst[port]) + for port in range(self.total_ports): + top_instances.append(self.row_addr_dff_inst[port]) + top_instances.append(self.control_logic_inst[port]) + if self.col_addr_dff: + top_instances.append(self.col_addr_dff_inst[port]) # for inst in top_instances: @@ -266,63 +271,66 @@ class sram_1bank(sram_base): def route_control_logic(self): """ Route the outputs from the control logic module """ - for n in self.control_logic_outputs: - src_pin = self.control_logic_inst.get_pin(n) - dest_pin = self.bank_inst.get_pin(n) - self.connect_rail_from_left_m2m3(src_pin, dest_pin) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=src_pin.rc(), - rotate=90) + for port in range(self.total_ports): + for signal in self.control_logic_outputs[port]: + src_pin = self.control_logic_inst[port].get_pin(signal) + dest_pin = self.bank_inst.get_pin(signal+"{}".format(port)) + self.connect_rail_from_left_m2m3(src_pin, dest_pin) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=src_pin.rc(), + rotate=90) def route_row_addr_dff(self): """ Connect the output of the row flops to the bank pins """ - for i in range(self.row_addr_size): - flop_name = "dout[{}]".format(i) - bank_name = "addr0[{}]".format(i+self.col_addr_size) - flop_pin = self.row_addr_dff_inst.get_pin(flop_name) - bank_pin = self.bank_inst.get_pin(bank_name) - flop_pos = flop_pin.center() - bank_pos = bank_pin.center() - mid_pos = vector(bank_pos.x,flop_pos.y) - self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos,bank_pos]) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=flop_pos, - rotate=90) + for port in range(self.total_ports): + for bit in range(self.row_addr_size): + flop_name = "dout[{}]".format(bit) + bank_name = "addr{0}[{1}]".format(port,bit+self.col_addr_size) + flop_pin = self.row_addr_dff_inst[port].get_pin(flop_name) + bank_pin = self.bank_inst.get_pin(bank_name) + flop_pos = flop_pin.center() + bank_pos = bank_pin.center() + mid_pos = vector(bank_pos.x,flop_pos.y) + self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos,bank_pos]) + self.add_via_center(layers=("metal2","via2","metal3"), + offset=flop_pos, + rotate=90) def route_col_addr_dff(self): """ Connect the output of the row flops to the bank pins """ + for port in range(self.total_ports): + bus_names = ["addr[{}]".format(x) for x in range(self.col_addr_size)] + col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1", + pitch=self.m1_pitch, + offset=self.col_addr_dff_inst[port].ul() + vector(0, self.m1_pitch), + names=bus_names, + length=self.col_addr_dff_inst[port].width) - bus_names = ["addr[{}]".format(x) for x in range(self.col_addr_size)] - col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1", - pitch=self.m1_pitch, - offset=self.col_addr_dff_inst.ul() + vector(0, self.m1_pitch), - names=bus_names, - length=self.col_addr_dff_inst.width) - - dff_names = ["dout[{}]".format(x) for x in range(self.col_addr_size)] - data_dff_map = zip(dff_names, bus_names) - self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_inst, col_addr_bus_offsets) - - bank_names = ["addr0[{}]".format(x) for x in range(self.col_addr_size)] - data_bank_map = zip(bank_names, bus_names) - self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets) + dff_names = ["dout[{}]".format(x) for x in range(self.col_addr_size)] + data_dff_map = zip(dff_names, bus_names) + self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_inst[port], col_addr_bus_offsets) + + bank_names = ["addr{0}[{1}]".format(port,x) for x in range(self.col_addr_size)] + data_bank_map = zip(bank_names, bus_names) + self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets) def route_data_dff(self): """ Connect the output of the data flops to the write driver """ # This is where the channel will start (y-dimension at least) - offset = self.data_dff_inst.ul() + vector(0, self.m1_pitch) + for port in range(self.total_write): + offset = self.data_dff_inst[port].ul() + vector(0, self.m1_pitch) - dff_names = ["dout[{}]".format(x) for x in range(self.word_size)] - bank_names = ["din0[{}]".format(x) for x in range(self.word_size)] + dff_names = ["dout[{}]".format(x) for x in range(self.word_size)] + 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_inst.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) + route_map = list(zip(bank_names, dff_names)) + dff_pins = {key: self.data_dff_inst[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) @@ -333,8 +341,8 @@ class sram_1bank(sram_base): will show these as ports in the extracted netlist. """ - for n in self.control_logic_outputs: - pin = self.control_logic_inst.get_pin(n) + for n in self.control_logic_outputs[0]: + pin = self.control_logic_inst[0].get_pin(n) self.add_label(text=n, layer=pin.layer, offset=pin.center()) diff --git a/compiler/sram_base.py b/compiler/sram_base.py index b2336cd4..e13bd58a 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -29,14 +29,18 @@ class sram_base(design): def add_pins(self): """ Add pins for entire SRAM. """ self.read_index = [] + self.port_id = [] port_number = 0 for port in range(OPTS.num_rw_ports): self.read_index.append("{}".format(port_number)) + self.port_id.append("rw") port_number += 1 for port in range(OPTS.num_w_ports): + self.port_id.append("w") port_number += 1 for port in range(OPTS.num_r_ports): self.read_index.append("{}".format(port_number)) + self.port_id.append("r") port_number += 1 for port in range(self.total_write): @@ -47,15 +51,26 @@ class sram_base(design): for bit in range(self.addr_size): self.add_pin("ADDR{0}[{1}]".format(port,bit),"INPUT") - # These are used to create the physical pins too - self.control_logic_inputs=self.control_logic.get_inputs() - self.control_logic_outputs=self.control_logic.get_outputs() + # These are used to create the physical pins + self.control_logic_inputs = [] + self.control_logic_outputs = [] + for port in range(self.total_ports): + if self.port_id[port] == "rw": + self.control_logic_inputs.append(self.control_logic_rw.get_inputs()) + self.control_logic_outputs.append(self.control_logic_rw.get_outputs()) + elif self.port_id[port] == "w": + self.control_logic_inputs.append(self.control_logic_w.get_inputs()) + self.control_logic_outputs.append(self.control_logic_w.get_outputs()) + else: + self.control_logic_inputs.append(self.control_logic_r.get_inputs()) + self.control_logic_outputs.append(self.control_logic_r.get_outputs()) - #self.add_pin_list(self.control_logic_inputs,"INPUT") - self.add_pin("csb","INPUT") + for port in range(self.total_ports): + self.add_pin("csb{}".format(port),"INPUT") for port in range(self.total_write): self.add_pin("web{}".format(port),"INPUT") - self.add_pin("clk","INPUT") + for port in range(self.total_ports): + self.add_pin("clk{}".format(port),"INPUT") for port in range(self.total_read): for bit in range(self.word_size): @@ -75,6 +90,7 @@ class sram_base(design): # This is for the lib file if we don't create layout self.width=0 self.height=0 + def create_layout(self): """ Layout creation """ @@ -116,65 +132,67 @@ class sram_base(design): "Bank is too small compared to control logic.") - def add_busses(self): """ Add the horizontal and vertical busses """ # Vertical bus # The order of the control signals on the control bus: - self.control_bus_names = ["clk_buf", "clk_buf_bar", "w_en0", "s_en"] - self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2", - pitch=self.m2_pitch, - offset=self.vertical_bus_offset, - names=self.control_bus_names, - length=self.vertical_bus_height) + self.control_bus_names = [] + for port in range(self.total_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.vert_control_bus_positions = self.create_vertical_bus(layer="metal2", + pitch=self.m2_pitch, + offset=self.vertical_bus_offset, + names=self.control_bus_names[port], + length=self.vertical_bus_height) - self.addr_bus_names=["A[{}]".format(i) for i in range(self.addr_size)] - self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2", - pitch=self.m2_pitch, - offset=self.addr_bus_offset, - names=self.addr_bus_names, - length=self.addr_bus_height)) + self.addr_bus_names=["A{0}[{1}]".format(port,i) for i in range(self.addr_size)] + self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2", + pitch=self.m2_pitch, + offset=self.addr_bus_offset, + names=self.addr_bus_names, + length=self.addr_bus_height)) - - self.bank_sel_bus_names = ["bank_sel[{}]".format(i) for i in range(self.num_banks)] - self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2", - pitch=self.m2_pitch, - offset=self.bank_sel_bus_offset, - names=self.bank_sel_bus_names, - length=self.vertical_bus_height)) - + + self.bank_sel_bus_names = ["bank_sel{0}[{1}]".format(port,i) for i in range(self.num_banks)] + self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2", + pitch=self.m2_pitch, + offset=self.bank_sel_bus_offset, + names=self.bank_sel_bus_names, + length=self.vertical_bus_height)) + - # Horizontal data bus - self.data_bus_names = ["DATA[{}]".format(i) for i in range(self.word_size)] - self.data_bus_positions = self.create_horizontal_pin_bus(layer="metal3", - pitch=self.m3_pitch, - offset=self.data_bus_offset, - names=self.data_bus_names, - length=self.data_bus_width) - - # Horizontal control logic bus - # vdd/gnd in bus go along whole SRAM - # FIXME: Fatten these wires? - self.horz_control_bus_positions = self.create_horizontal_bus(layer="metal1", - pitch=self.m1_pitch, - offset=self.supply_bus_offset, - names=["vdd"], - length=self.supply_bus_width) - # The gnd rail must not be the entire width since we protrude the right-most vdd rail up for - # the decoder in 4-bank SRAMs - self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1", - pitch=self.m1_pitch, - offset=self.supply_bus_offset+vector(0,self.m1_pitch), - names=["gnd"], - length=self.supply_bus_width)) - self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1", - pitch=self.m1_pitch, - offset=self.control_bus_offset, - names=self.control_bus_names, - length=self.control_bus_width)) - - + # Horizontal data bus + self.data_bus_names = ["DATA{0}[{1}]".format(port,i) for i in range(self.word_size)] + self.data_bus_positions = self.create_horizontal_pin_bus(layer="metal3", + pitch=self.m3_pitch, + offset=self.data_bus_offset, + names=self.data_bus_names, + length=self.data_bus_width) + # Horizontal control logic bus + # vdd/gnd in bus go along whole SRAM + # FIXME: Fatten these wires? + self.horz_control_bus_positions = self.create_horizontal_bus(layer="metal1", + pitch=self.m1_pitch, + offset=self.supply_bus_offset, + names=["vdd"], + length=self.supply_bus_width) + # The gnd rail must not be the entire width since we protrude the right-most vdd rail up for + # the decoder in 4-bank SRAMs + self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1", + pitch=self.m1_pitch, + offset=self.supply_bus_offset+vector(0,self.m1_pitch), + names=["gnd"], + length=self.supply_bus_width)) + self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1", + pitch=self.m1_pitch, + offset=self.control_bus_offset, + names=self.control_bus_names[port], + length=self.control_bus_width)) def route_vdd_gnd(self): @@ -218,33 +236,41 @@ class sram_base(design): self.msb_decoder = self.bank.decoder.pre2_4 self.add_mod(self.msb_decoder) + def add_modules(self): """ Create all the modules that will be used """ c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell = self.mod_bitcell() - c = reload(__import__(OPTS.control_logic)) - self.mod_control_logic = getattr(c, OPTS.control_logic) + #c = reload(__import__(OPTS.control_logic)) + #self.mod_control_logic = getattr(c, OPTS.control_logic) from control_logic import control_logic - # Create the control logic module - self.control_logic = self.mod_control_logic(num_rows=self.num_rows) - self.add_mod(self.control_logic) + # Create the control logic module for each port type + if OPTS.num_rw_ports>0: + self.control_logic = self.control_logic_rw = control_logic(num_rows=self.num_rows, port="rw") + self.add_mod(self.control_logic_rw) + if OPTS.num_w_ports>0: + self.control_logic_w = control_logic(num_rows=self.num_rows, port="w") + self.add_mod(self.control_logic_w) + if OPTS.num_r_ports>0: + self.control_logic_r = control_logic(num_rows=self.num_rows, port="r") + self.add_mod(self.control_logic_r) # Create the address and control flops (but not the clk) from dff_array import dff_array - self.row_addr_dff = dff_array(name="row_addr_dff", rows=self.row_addr_size*self.total_ports, columns=1) + self.row_addr_dff = dff_array(name="row_addr_dff", rows=self.row_addr_size, columns=1) self.add_mod(self.row_addr_dff) if self.col_addr_size > 0: - self.col_addr_dff = dff_array(name="col_addr_dff", rows=1, columns=self.col_addr_size*self.total_ports) + self.col_addr_dff = dff_array(name="col_addr_dff", rows=1, columns=self.col_addr_size) self.add_mod(self.col_addr_dff) else: self.col_addr_dff = None - self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size*self.total_write) + self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size) self.add_mod(self.data_dff) # Create the bank module (up to four are instantiated) @@ -263,7 +289,6 @@ class sram_base(design): self.supply_rail_pitch = self.bank.supply_rail_pitch - def create_bank(self,bank_num): """ Create a bank """ self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num), @@ -282,10 +307,14 @@ class sram_base(design): if(self.num_banks > 1): for port in range(self.total_ports): temp.append("bank_sel{0}[{1}]".format(port,bank_num)) - temp.append("s_en") + for port in range(self.total_read): + temp.append("s_en{0}".format(self.read_index[port])) for port in range(self.total_write): temp.append("w_en{0}".format(port)) - temp.extend(["clk_buf_bar","clk_buf" , "vdd", "gnd"]) + for port in range(self.total_ports): + temp.append("clk_buf_bar{0}".format(port)) + temp.append("clk_buf{0}".format(port)) + temp.extend(["vdd", "gnd"]) self.connect_inst(temp) return self.bank_insts[-1] @@ -324,73 +353,87 @@ class sram_base(design): def create_row_addr_dff(self): """ Add all address flops for the main decoder """ - inst = self.add_inst(name="row_address", - mod=self.row_addr_dff) - - # inputs, outputs/output/bar - inputs = [] - outputs = [] + insts = [] for port in range(self.total_ports): - for i in range(self.row_addr_size): - inputs.append("ADDR{}[{}]".format(port,i+self.col_addr_size)) - outputs.append("A{}[{}]".format(port,i+self.col_addr_size)) + insts.append(self.add_inst(name="row_address{}".format(port), + mod=self.row_addr_dff)) + + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.row_addr_size): + inputs.append("ADDR{}[{}]".format(port,bit+self.col_addr_size)) + outputs.append("A{}[{}]".format(port,bit+self.col_addr_size)) + + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + + return insts - self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) - return inst def create_col_addr_dff(self): """ Add and place all address flops for the column decoder """ - inst = self.add_inst(name="col_address", - mod=self.col_addr_dff) - - # inputs, outputs/output/bar - inputs = [] - outputs = [] + insts = [] for port in range(self.total_ports): - for i in range(self.col_addr_size): - inputs.append("ADDR{}[{}]".format(port,i)) - outputs.append("A{}[{}]".format(port,i)) + insts.append(self.add_inst(name="col_address{}".format(port), + mod=self.col_addr_dff)) + + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.col_addr_size): + inputs.append("ADDR{}[{}]".format(port,bit)) + outputs.append("A{}[{}]".format(port,bit)) - self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) - return inst - + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + + return insts + + def create_data_dff(self): """ Add and place all data flops """ - inst = self.add_inst(name="data_dff", - mod=self.data_dff) - - # inputs, outputs/output/bar - inputs = [] - outputs = [] + insts = [] for port in range(self.total_write): - for i in range(self.word_size): - inputs.append("DIN{}[{}]".format(port,i)) - outputs.append("BANK_DIN{}[{}]".format(port,i)) + insts.append(self.add_inst(name="data_dff{}".format(port), + mod=self.data_dff)) + + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.word_size): + inputs.append("DIN{}[{}]".format(port,bit)) + outputs.append("BANK_DIN{}[{}]".format(port,bit)) + + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + + return insts - self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) - return inst def create_control_logic(self): """ Add and place control logic """ - inst = self.add_inst(name="control", - mod=self.control_logic) + insts = [] + for port in range(self.total_ports): + if self.port_id[port] == "rw": + mod = self.control_logic_rw + elif self.port_id[port] == "w": + mod = self.control_logic_w + else: + mod = self.control_logic_r + + insts.append(self.add_inst(name="control{}".format(port), + mod=mod)) + + temp = ["csb{}".format(port)] + if (self.port_id[port] == "rw") or (self.port_id[port] == "w"): + temp.append("web{}".format(port)) + temp.append("clk{}".format(port)) + if (self.port_id[port] == "rw") or (self.port_id[port] == "r"): + temp.append("s_en{}".format(port)) + if (self.port_id[port] == "rw") or (self.port_id[port] == "w"): + temp.append("w_en{}".format(port)) + temp.extend(["clk_buf_bar{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"]) + self.connect_inst(temp) - temp = ["csb"] - for port in range(self.total_write): - temp.append("web{}".format(port)) - temp.extend(["clk", "s_en"]) - for port in range(self.total_write): - temp.append("w_en{}".format(port)) - temp.extend(["clk_buf_bar", "clk_buf", "vdd", "gnd"]) - self.connect_inst(temp) - - #self.connect_inst(self.control_logic_inputs + self.control_logic_outputs + ["vdd", "gnd"]) - return inst - - - - - + return insts def connect_rail_from_left_m2m3(self, src_pin, dest_pin): @@ -401,14 +444,14 @@ class sram_base(design): self.add_via_center(layers=("metal2","via2","metal3"), offset=src_pin.rc(), rotate=90) - + + def connect_rail_from_left_m2m1(self, src_pin, dest_pin): """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ in_pos = src_pin.rc() out_pos = vector(dest_pin.cx(), in_pos.y) self.add_wire(("metal2","via1","metal1"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)]) - def sp_write(self, sp_name): # Write the entire spice of the object to the file @@ -434,6 +477,7 @@ class sram_base(design): del usedMODS sp.close() + def analytical_delay(self,slew,load): """ LH and HL are the same in analytical model. """ return self.bank.analytical_delay(slew,load)