diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index cbd9b8d5..8d07a61a 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -18,7 +18,7 @@ from .charutils import * import utils from globals import OPTS from .simulation import simulation -from .delay import delay +# from .delay import delay import graph_util from sram_factory import factory @@ -40,6 +40,9 @@ class functional(simulation): else: self.num_wmasks = 0 + if not self.num_spare_cols: + self.num_spare_cols = 0 + self.set_corner(corner) self.set_spice_constants() self.set_stimulus_variables() @@ -86,6 +89,7 @@ class functional(simulation): if port in self.write_ports: checks.append((self.data_value[port],"data")) checks.append((self.wmask_value[port],"wmask")) + checks.append((self.spare_wen_value[port],"spare_wen")) for (val, name) in checks: debug.check(len(self.cycle_times)==len(val), @@ -226,7 +230,7 @@ class functional(simulation): # Extract dout values from spice timing.lis for (word, dout_port, eo_period, check) in self.read_check: sp_read_value = "" - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check)) if value > self.v_high: sp_read_value = "1" + sp_read_value @@ -281,13 +285,18 @@ class functional(simulation): def gen_data(self): """ Generates a random word to write. """ - random_value = random.randint(0,(2**self.word_size)-1) + if not self.num_spare_cols: + random_value = random.randint(0,(2**(self.word_size))-1) + else: + random_value1 = random.randint(0,(2**(self.word_size))-1) + random_value2 = random.randint(0,(2**(self.num_spare_cols))-1) + random_value = random_value1 + random_value2 data_bits = self.convert_to_bin(random_value,False) return data_bits def gen_addr(self): """ Generates a random address value to write to. """ - if (self.num_spare_rows == 0): + if self.num_spare_rows==0: random_value = random.randint(0,(2**self.addr_size)-1) else: random_value = random.randint(0,((2**(self.addr_size-1)-1))+(self.num_spare_rows * self.words_per_row)) @@ -307,8 +316,7 @@ class functional(simulation): if(is_addr): expected_value = self.addr_size else: - - expected_value = self.word_size + expected_value = self.word_size + self.num_spare_cols for i in range (expected_value - len(new_value)): new_value = "0" + new_value @@ -337,7 +345,7 @@ class functional(simulation): # Add load capacitance to each of the read ports self.sf.write("\n* SRAM output loads\n") for port in self.read_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): sig_name="{0}{1}_{2} ".format(self.dout_name, port, bit) self.sf.write("CD{0}{1} {2} 0 {3}f\n".format(port, bit, sig_name, self.load)) @@ -357,7 +365,7 @@ class functional(simulation): # Generate data input bits self.sf.write("\n* Generation of data and address signals\n") for port in self.write_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): sig_name="{0}{1}_{2} ".format(self.din_name, port, bit) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05) @@ -386,6 +394,15 @@ class functional(simulation): self.stim.gen_pwl(sig_name, self.cycle_times, self.wmask_values[port][bit], self.period, self.slew, 0.05) + # Generate spare enable bits (for spare cols) + for port in self.write_ports: + if self.num_spare_cols: + self.sf.write("\n* Generation of spare enable signals\n") + for bit in range(self.num_spare_cols): + sig_name = "SPARE_WEN{0}_{1} ".format(port, bit) + self.stim.gen_pwl(sig_name, self.cycle_times, self.spare_wen_values[port][bit], self.period, + self.slew, 0.05) + # Generate CLK signals for port in self.all_ports: self.stim.gen_pulse(sig_name="{0}{1}".format("clk", port), @@ -401,7 +418,7 @@ class functional(simulation): for (word, dout_port, eo_period, check) in self.read_check: t_intital = eo_period - 0.01*self.period t_final = eo_period + 0.01*self.period - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): self.stim.gen_meas_value(meas_name="V{0}_{1}ck{2}".format(dout_port,bit,check), dout="{0}_{1}".format(dout_port,bit), t_intital=t_intital, diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index e0cc6195..9027fca1 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -26,6 +26,10 @@ class simulation(): self.addr_size = self.sram.addr_size self.write_size = self.sram.write_size self.num_spare_rows = self.sram.num_spare_rows + if not self.sram.num_spare_cols: + self.num_spare_cols = 0 + else: + self.num_spare_cols = self.sram.num_spare_cols self.sp_file = spfile self.all_ports = self.sram.all_ports @@ -38,7 +42,6 @@ class simulation(): else: self.num_wmasks = 0 - def set_corner(self,corner): """ Set the corner values """ self.corner = corner @@ -61,10 +64,10 @@ class simulation(): self.pins = self.gen_pin_names(port_signal_names=(self.addr_name,self.din_name,self.dout_name), port_info=(len(self.all_ports),self.write_ports,self.read_ports), abits=self.addr_size, - dbits=self.word_size) + dbits=self.word_size + self.num_spare_cols) debug.check(len(self.sram.pins) == len(self.pins), "Number of pins generated for characterization \ - do match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins, + do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins, self.pins)) #This is TODO once multiport control has been finalized. #self.control_name = "CSB" @@ -82,11 +85,13 @@ class simulation(): self.addr_value = {port:[] for port in self.all_ports} self.data_value = {port:[] for port in self.write_ports} self.wmask_value = {port:[] for port in self.write_ports} + self.spare_wen_value = {port:[] for port in self.write_ports} # Three dimensional list to handle each addr and data bits for each port over the number of checks self.addr_values = {port:[[] for bit in range(self.addr_size)] for port in self.all_ports} - self.data_values = {port:[[] for bit in range(self.word_size)] for port in self.write_ports} + self.data_values = {port:[[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports} self.wmask_values = {port:[[] for bit in range(self.num_wmasks)] for port in self.write_ports} + self.spare_wen_values = {port:[[] for bit in range(self.num_spare_cols)] for port in self.write_ports} # For generating comments in SPICE stimulus self.cycle_comments = [] @@ -113,10 +118,10 @@ class simulation(): def add_data(self, data, port): """ Add the array of data values """ - debug.check(len(data)==self.word_size, "Invalid data word size.") + debug.check(len(data)==(self.word_size + self.num_spare_cols), "Invalid data word size.") self.data_value[port].append(data) - bit = self.word_size - 1 + bit = self.word_size + self.num_spare_cols - 1 for c in data: if c=="0": self.data_values[port][bit].append(0) @@ -137,10 +142,7 @@ class simulation(): if c=="0": self.addr_values[port][bit].append(0) elif c=="1": - if((self.num_spare_rows != 0) and (bit == (self.addr_size - 1))): - self.addr_values[port][bit].append(0) - else: - self.addr_values[port][bit].append(1) + self.addr_values[port][bit].append(1) else: debug.error("Non-binary address string",1) bit -= 1 @@ -161,7 +163,21 @@ class simulation(): debug.error("Non-binary wmask string", 1) bit -= 1 - + def add_spare_wen(self, spare_wen, port): + """ Add the array of spare write enable values (for spare cols) """ + debug.check(len(spare_wen) == self.num_spare_cols, "Invalid spare enable size.") + + self.spare_wen_value[port].append(spare_wen) + bit = self.num_spare_cols - 1 + for c in spare_wen: + if c == "0": + self.spare_wen_values[port][bit].append(0) + elif c == "1": + self.spare_wen_values[port][bit].append(1) + else: + debug.error("Non-binary spare enable signal string", 1) + bit -= 1 + def add_write(self, comment, address, data, wmask, port): """ Add the control values for a write cycle. """ debug.check(port in self.write_ports, @@ -178,6 +194,7 @@ class simulation(): self.add_data(data,port) self.add_address(address,port) self.add_wmask(wmask,port) + self.add_spare_wen("1" * self.num_spare_cols, port) #Add noops to all other ports. for unselected_port in self.all_ports: @@ -197,6 +214,7 @@ class simulation(): self.t_current += self.period self.add_control_one_port(port, "read") self.add_address(address, port) + self.add_spare_wen("0" * self.num_spare_cols, port) # If the port is also a readwrite then add # the same value as previous cycle @@ -204,7 +222,7 @@ class simulation(): try: self.add_data(self.data_value[port][-1], port) except: - self.add_data("0"*self.word_size, port) + self.add_data("0"*(self.word_size + self.num_spare_cols), port) try: self.add_wmask(self.wmask_value[port][-1], port) except: @@ -239,6 +257,7 @@ class simulation(): self.add_data(data, port) self.add_address(address, port) self.add_wmask(wmask, port) + self.add_spare_wen("1" * self.num_spare_cols, port) def add_read_one_port(self, comment, address, port): """ Add the control values for a read cycle. Does not increment the period. """ @@ -250,13 +269,14 @@ class simulation(): self.add_control_one_port(port, "read") self.add_address(address, port) + self.add_spare_wen("0" * self.num_spare_cols, port) # If the port is also a readwrite then add # the same value as previous cycle if port in self.write_ports: try: self.add_data(self.data_value[port][-1], port) except: - self.add_data("0"*self.word_size, port) + self.add_data("0"*(self.word_size + self.num_spare_cols), port) try: self.add_wmask(self.wmask_value[port][-1], port) except: @@ -266,6 +286,7 @@ class simulation(): def add_noop_one_port(self, port): """ Add the control values for a noop to a single port. Does not increment the period. """ self.add_control_one_port(port, "noop") + self.add_spare_wen("0" * self.num_spare_cols, port) try: self.add_address(self.addr_value[port][-1], port) @@ -278,7 +299,7 @@ class simulation(): try: self.add_data(self.data_value[port][-1], port) except: - self.add_data("0"*self.word_size, port) + self.add_data("0"*(self.word_size + self.num_spare_cols), port) try: self.add_wmask(self.wmask_value[port][-1], port) except: @@ -374,6 +395,11 @@ class simulation(): for port in write_index: for bit in range(self.num_wmasks): pin_names.append("WMASK{0}_{1}".format(port,bit)) + + if self.num_spare_cols: + for port in write_index: + for bit in range(self.num_spare_cols): + pin_names.append("SPARE_WEN{0}_{1}".format(port,bit)) for read_output in read_index: for i in range(dbits): diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 40eabde6..ad8b34df 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -105,7 +105,7 @@ class bank(design.design): for bit in range(self.num_wmasks): self.add_pin("bank_wmask{0}_{1}".format(port, bit), "INPUT") for bit in range(self.num_spare_cols): - self.add_pin("spare_wen{0}_{1}".format(port, bit), "INPUT") + self.add_pin("bank_spare_wen{0}_{1}".format(port, bit), "INPUT") for port in self.all_ports: self.add_pin("wl_en{0}".format(port), "INPUT") self.add_pin("vdd", "POWER") @@ -442,7 +442,7 @@ class bank(design.design): for bit in range(self.num_wmasks): temp.append("bank_wmask{0}_{1}".format(port, bit)) for bit in range(self.num_spare_cols): - temp.append("spare_wen{0}_{1}".format(port, bit)) + temp.append("bank_spare_wen{0}_{1}".format(port, bit)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -725,15 +725,15 @@ class bank(design.design): din_name = "din{0}_{1}".format(port, row) self.copy_layout_pin(self.port_data_inst[port], data_name, din_name) - if self.word_size: + if self.write_size: for row in range(self.num_wmasks): wmask_name = "bank_wmask_{}".format(row) bank_wmask_name = "bank_wmask{0}_{1}".format(port, row) self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name) for col in range(self.num_spare_cols): - sparecol_name = "spare_wen{}".format(col) - bank_sparecol_name = "spare_wen{0}_{1}".format(port, col) + sparecol_name = "bank_spare_wen{}".format(col) + bank_sparecol_name = "bank_spare_wen{0}_{1}".format(port, col) self.copy_layout_pin(self.port_data_inst[port], sparecol_name, bank_sparecol_name) def channel_route_bitlines(self, inst1, inst2, num_bits, diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index e09a6615..41970587 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -107,7 +107,7 @@ class control_logic(design.design): # clk_buf drives a flop for every address addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2) # plus data flops and control flops - num_flops = addr_flops + self.word_size + self.num_control_signals + num_flops = addr_flops + self.word_size + self.num_spare_cols + self.num_control_signals # each flop internally has a FO 5 approximately # plus about 5 fanouts for the control logic clock_fanout = 5 * num_flops + 5 @@ -135,7 +135,7 @@ class control_logic(design.design): # s_en drives every sense amp self.sen_and3 = factory.create(module_type="pand3", - size=self.word_size, + size=self.word_size + self.num_spare_cols, height=dff_height) self.add_mod(self.sen_and3) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 968b4605..483cea93 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -138,7 +138,7 @@ class port_data(design.design): for bit in range(self.num_wmasks): self.add_pin("bank_wmask_{}".format(bit), "INPUT") for bit in range(self.num_spare_cols): - self.add_pin("spare_wen{}".format(bit), "INPUT") + self.add_pin("bank_spare_wen{}".format(bit), "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") @@ -387,12 +387,12 @@ class port_data(design.design): for i in range(self.num_wmasks): temp.append("wdriver_sel_{}".format(i)) for i in range(self.num_spare_cols): - temp.append("spare_wen{}".format(i)) + temp.append("bank_spare_wen{}".format(i)) elif self.num_spare_cols and not self.write_size: temp.append("w_en") for i in range(self.num_spare_cols): - temp.append("spare_wen{}".format(i)) + temp.append("bank_spare_wen{}".format(i)) else: temp.append("w_en") temp.extend(["vdd", "gnd"]) @@ -627,9 +627,9 @@ class port_data(design.design): start_bit=0 if self.port==0: - off=1 + off = 1 else: - off=0 + off = 0 if self.num_spare_cols != 0 and self.col_addr_size>0: @@ -722,11 +722,11 @@ class port_data(design.design): self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit), "wdriver_sel_{}".format(bit)) for bit in range(self.num_spare_cols): # Add spare columns' en_{} pins - self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit + self.num_wmasks), "spare_wen{}".format(bit)) + self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit + self.num_wmasks), "bank_spare_wen{}".format(bit)) elif self.num_spare_cols and not self.write_mask_and_array_inst: self.copy_layout_pin(self.write_driver_array_inst, "en_0", "w_en") for bit in range(self.num_spare_cols): - self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit + 1), "spare_wen{}".format(bit)) + self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit + 1), "bank_spare_wen{}".format(bit)) else: self.copy_layout_pin(self.write_driver_array_inst, "en", "w_en") if self.write_mask_and_array_inst: diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 48564e86..b9f6c124 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -142,7 +142,7 @@ class write_driver_array(design.design): offset = self.num_wmasks else: offset = 1 - name = "write_driver{}".format(index) + name = "write_driver{}".format(self.columns + i) self.driver_insts[index]=self.add_inst(name=name, mod=self.driver) @@ -234,27 +234,30 @@ class write_driver_array(design.design): for i in range(self.num_spare_cols): inst = self.driver_insts[self.word_size + i] + en_pin = inst.get_pin(inst.mod.en_name) self.add_layout_pin(text=self.en_name + "_{0}".format(i + self.num_wmasks), layer="m1", - offset=inst.get_pin(inst.mod.en_name).ll(), - width=self.single_col_width - inst.get_pin(inst.mod.en_name).width()) + offset=en_pin.ll(), + width=self.driver.width - en_pin.width()) elif self.num_spare_cols and not self.write_size: # shorten enable rail to accomodate those for spare write drivers inst = self.driver_insts[0] + en_pin = inst.get_pin(inst.mod.en_name) self.add_layout_pin(text=self.en_name + "_{0}".format(0), layer="m1", - offset=inst.get_pin(inst.mod.en_name).ll(), - width=self.width_regular_cols - (self.words_per_row * inst.get_pin(inst.mod.en_name).width())) + offset=en_pin.ll(), + width=self.width_regular_cols - self.words_per_row * en_pin.width()) # individual enables for every spare write driver for i in range(self.num_spare_cols): inst = self.driver_insts[self.word_size + i] + en_pin = inst.get_pin(inst.mod.en_name) self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1), layer="m1", - offset=inst.get_pin(inst.mod.en_name).ll(), - width=self.single_col_width - inst.get_pin(inst.mod.en_name).width()) + offset=en_pin.ll(), + width=self.driver.width - en_pin.width()) else: inst = self.driver_insts[0] diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 4af19f98..11a17129 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -39,6 +39,11 @@ class sram_1bank(sram_base): else: self.data_dff_insts = self.create_data_dff() + if self.num_spare_cols: + self.spare_wen_dff_insts = self.create_spare_wen_dff() + else: + self.num_spare_cols = 0 + def place_instances(self): """ This places the instances for a single bank SRAM with control @@ -57,6 +62,7 @@ class sram_1bank(sram_base): row_addr_pos = [None] * len(self.all_ports) col_addr_pos = [None] * len(self.all_ports) wmask_pos = [None] * len(self.all_ports) + spare_wen_pos = [None] * len(self.all_ports) data_pos = [None] * len(self.all_ports) # These positions utilize the channel route sizes. @@ -69,9 +75,21 @@ class sram_1bank(sram_base): self.data_bus_size = self.m4_nonpref_pitch * (self.word_size + self.num_spare_cols) + self.data_bus_gap self.wmask_bus_gap = self.m2_nonpref_pitch * 2 self.wmask_bus_size = self.m2_nonpref_pitch * (max(self.num_wmasks + 1, self.col_addr_size + 1)) + self.wmask_bus_gap + if self.num_spare_cols: + self.spare_wen_bus_gap = self.m2_nonpref_pitch * 2 + self.spare_wen_bus_size = self.m2_nonpref_pitch * (max(self.num_spare_cols + 1, self.col_addr_size + 1)) + self.spare_wen_bus_gap + else: + self.spare_wen_bus_size = 0 + + elif self.num_spare_cols and not self.write_size: + self.data_bus_gap = self.m4_nonpref_pitch * 2 + self.data_bus_size = self.m4_nonpref_pitch * (self.word_size + self.num_spare_cols) + self.data_bus_gap + self.spare_wen_bus_gap = self.m2_nonpref_pitch * 2 + self.spare_wen_bus_size = self.m2_nonpref_pitch * (max(self.num_spare_cols + 1, self.col_addr_size + 1)) + self.spare_wen_bus_gap + else: self.data_bus_gap = self.m3_nonpref_pitch * 2 - self.data_bus_size = self.m3_nonpref_pitch * (max(self.word_size + self.num_spare_cols + 1, self.col_addr_size + 1)) + self.data_bus_gap + self.data_bus_size = self.m3_nonpref_pitch * (max(self.word_size + 1, self.col_addr_size + 1)) + self.data_bus_gap self.col_addr_bus_gap = self.m2_nonpref_pitch * 2 self.col_addr_bus_size = self.m2_nonpref_pitch * (self.col_addr_size) + self.col_addr_bus_gap @@ -81,34 +99,57 @@ class sram_1bank(sram_base): if port in self.write_ports: if self.write_size: + bus_size = max(self.wmask_bus_size, self.spare_wen_bus_size) # Add the write mask flops below the write mask AND array. wmask_pos[port] = vector(self.bank.bank_array_ll.x, - - self.wmask_bus_size - self.dff.height) + - bus_size - self.dff.height) self.wmask_dff_insts[port].place(wmask_pos[port]) # Add the data flops below the write mask flops. data_pos[port] = vector(self.bank.bank_array_ll.x, - - self.data_bus_size - self.wmask_bus_size - 2 * self.dff.height) + - self.data_bus_size - bus_size - 2 * self.dff.height) self.data_dff_insts[port].place(data_pos[port]) + + #Add spare write enable flops to the right of write mask flops + if self.num_spare_cols: + spare_wen_pos[port] = vector(self.bank.bank_array_ll.x + self.wmask_dff_insts[port].width + self.bank.m2_gap, + - bus_size - self.dff.height) + self.spare_wen_dff_insts[port].place(spare_wen_pos[port]) + + elif self.num_spare_cols and not self.write_size: + # Add spare write enable flops below bank (lower right) + spare_wen_pos[port] = vector(self.bank.bank_array_ll.x, + - self.spare_wen_bus_size - self.dff.height) + self.spare_wen_dff_insts[port].place(spare_wen_pos[port]) + + # Add the data flops below the spare write enable flops. + data_pos[port] = vector(self.bank.bank_array_ll.x, + - self.data_bus_size - self.spare_wen_bus_size - 2 * self.dff.height) + self.data_dff_insts[port].place(data_pos[port]) + else: # Add the data flops below the bank to the right of the lower-left of bank array # This relies on the lower-left of the array 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. - if port in self.write_ports: - data_pos[port] = vector(self.bank.bank_array_ll.x, - -self.data_bus_size - self.dff.height) - self.data_dff_insts[port].place(data_pos[port]) + data_pos[port] = vector(self.bank.bank_array_ll.x, + -self.data_bus_size - self.dff.height) + self.data_dff_insts[port].place(data_pos[port]) + else: wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0) data_pos[port] = vector(self.bank.bank_array_ll.x, 0) + spare_wen_pos[port] = vector(self.bank.bank_array_ll.x, 0) # Add the col address flops below the bank to the left of the lower-left of bank array if self.col_addr_dff: if self.write_size: col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, - -self.wmask_bus_size - self.col_addr_dff_insts[port].height) + -bus_size - self.col_addr_dff_insts[port].height) + elif self.num_spare_cols and not self.write_size: + col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, + -self.spare_wen_bus_size - self.col_addr_dff_insts[port].height) else: col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, -self.data_bus_size - self.col_addr_dff_insts[port].height) @@ -134,15 +175,34 @@ class sram_1bank(sram_base): if port in self.write_ports: if self.write_size: - # Add the write mask flops below the write mask AND array. + bus_size = max(self.wmask_bus_size, self.spare_wen_bus_size) + # Add the write mask flops above the write mask AND array. wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width, - self.bank.height + self.wmask_bus_size + self.dff.height) + self.bank.height + bus_size + self.dff.height) self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX") - # Add the data flops below the write mask flops + # Add the data flops above the write mask flops data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, - self.bank.height + self.wmask_bus_size + self.data_bus_size + 2 * self.dff.height) + self.bank.height + bus_size + self.data_bus_size + 2 * self.dff.height) self.data_dff_insts[port].place(data_pos[port], mirror="MX") + + if self.num_spare_cols: + spare_wen_pos[port] = vector(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width + - self.spare_wen_dff_insts[port].width - self.bank.m2_gap, + self.bank.height + bus_size + self.dff.height)) + self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX") + + # Place dffs when spare cols is enabled + elif self.num_spare_cols and not self.write_size: + # Spare wen flops on the upper right, below data flops + spare_wen_pos[port] = vector(self.bank.bank_array_ur.x - self.spare_wen_dff_insts[port].width, + self.bank.height + self.spare_wen_bus_size + self.dff.height) + self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX") + # Add the data flops above the spare write enable flops + data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, + self.bank.height + self.spare_wen_bus_size + self.data_bus_size + 2 * self.dff.height) + self.data_dff_insts[port].place(data_pos[port], mirror="MX") + else: # Add the data flops above the bank to the left of the upper-right of bank array # This relies on the upper-right of the array of the bank @@ -157,7 +217,10 @@ class sram_1bank(sram_base): if self.col_addr_dff: if self.write_size: col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, - self.bank.height + self.wmask_bus_size + self.dff.height) + self.bank.height + bus_size + self.dff.height) + elif self.num_spare_cols and not self.write_size: + col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, + self.bank.height + self.spare_wen_bus_size + self.dff.height) else: col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, self.bank.height + self.data_bus_size + self.dff.height) @@ -218,9 +281,10 @@ class sram_1bank(sram_base): self.copy_layout_pin(self.wmask_dff_insts[port], "din_{}".format(bit), "wmask{0}[{1}]".format(port, bit)) + for bit in range(self.num_spare_cols): - self.copy_layout_pin(self.bank_inst, - "spare_wen{0}_{1}".format(port, bit), + self.copy_layout_pin(self.spare_wen_dff_insts[port], + "din_{}".format(bit), "spare_wen{0}[{1}]".format(port, bit)) def route_layout(self): @@ -242,6 +306,9 @@ class sram_1bank(sram_base): if self.write_size: self.route_wmask_dff() + if self.num_spare_cols: + self.route_spare_wen_dff() + def route_clk(self): """ Route the clock network """ @@ -308,6 +375,15 @@ class sram_1bank(sram_base): self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height)) self.add_wire(self.m2_stack[::-1], [wmask_dff_clk_pos, mid_pos, clk_steiner_pos]) + if self.num_spare_cols: + spare_wen_dff_clk_pin = self.spare_wen_dff_insts[port].get_pin("clk") + spare_wen_dff_clk_pos = spare_wen_dff_clk_pin.center() + mid_pos = vector(clk_steiner_pos.x, spare_wen_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("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height)) + self.add_wire(self.m2_stack[::-1], [spare_wen_dff_clk_pos, mid_pos, clk_steiner_pos]) + def route_control_logic(self): """ Route the control logic pins that are not inputs """ @@ -373,20 +449,14 @@ 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: - if self.write_size: - if port % 2: - offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size) - else: - offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap) + if port % 2: + offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size) else: - if port % 2: - offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size) - else: - offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap) + offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap) dff_names = ["dout_{}".format(x) for x in range(self.word_size + self.num_spare_cols)] dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] - if self.write_size: + if self.write_size or self.num_spare_cols: for x in dff_names: pin = self.data_dff_insts[port].get_pin(x) pin_offset = pin.center() @@ -399,7 +469,7 @@ class sram_1bank(sram_base): bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] - if self.write_size: + if self.write_size or self.num_spare_cols: for x in bank_names: pin = self.bank_inst.get_pin(x) if port % 2: @@ -411,7 +481,7 @@ class sram_1bank(sram_base): offset=pin_offset) route_map = list(zip(bank_pins, dff_pins)) - if self.write_size: + if self.write_size or self.num_spare_cols: layer_stack = self.m3_stack else: layer_stack = self.m1_stack @@ -448,6 +518,36 @@ class sram_1bank(sram_base): self.create_horizontal_channel_route(netlist=route_map, offset=offset, layer_stack=self.m1_stack) + def route_spare_wen_dff(self): + """ Connect the output of the spare write enable flops to the spare write drivers """ + # This is where the channel will start (y-dimension at least) + for port in self.write_ports: + if port % 2: + # for port 0 + offset = self.spare_wen_dff_insts[port].ll() - vector(0, self.spare_wen_bus_size) + else: + offset = self.spare_wen_dff_insts[port].ul() + vector(0, self.spare_wen_bus_gap) + + dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] + dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] + for x in dff_names: + offset_pin = self.spare_wen_dff_insts[port].get_pin(x).center() + self.add_via_center(layers=self.m1_stack, + offset=offset_pin, + directions=("V", "V")) + + bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + for x in bank_names: + offset_pin = self.bank_inst.get_pin(x).center() + self.add_via_center(layers=self.m1_stack, + offset=offset_pin) + + route_map = list(zip(bank_pins, dff_pins)) + self.create_horizontal_channel_route(netlist=route_map, + offset=offset, + layer_stack=self.m1_stack) + def add_lvs_correspondence_points(self): """ @@ -470,6 +570,9 @@ class sram_1bank(sram_base): if self.write_size: for inst in self.wmask_dff_insts: self.graph_inst_exclude.add(inst) + if self.num_spare_cols: + for inst in self.spare_wen_dff_insts: + self.graph_inst_exclude.add(inst) def graph_exclude_addr_dff(self): """Removes data dff from search graph. """ diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 0ca569af..8002b2f1 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -285,6 +285,10 @@ class sram_base(design, verilog, lef): if self.write_size: self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks) self.add_mod(self.wmask_dff) + + if self.num_spare_cols: + self.spare_wen_dff = factory.create("dff_array", module_name="spare_wen_dff", rows=1, columns=self.num_spare_cols) + self.add_mod(self.spare_wen_dff) # Create the bank module (up to four are instantiated) self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank") @@ -358,7 +362,7 @@ class sram_base(design, verilog, lef): for bit in range(self.num_wmasks): temp.append("bank_wmask{}[{}]".format(port, bit)) for bit in range(self.num_spare_cols): - temp.append("spare_wen{0}[{1}]".format(port, bit)) + temp.append("bank_spare_wen{0}[{1}]".format(port, bit)) for port in self.all_ports: temp.append("wl_en{0}".format(port)) temp.extend(["vdd", "gnd"]) @@ -475,7 +479,29 @@ class sram_base(design, verilog, lef): self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) return insts + + def create_spare_wen_dff(self): + """ Add all spare write enable flops """ + insts = [] + for port in self.all_ports: + if port in self.write_ports: + insts.append(self.add_inst(name="spare_wen_dff{}".format(port), + mod=self.spare_wen_dff)) + else: + insts.append(None) + continue + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.num_spare_cols): + inputs.append("spare_wen{}[{}]".format(port, bit)) + outputs.append("bank_spare_wen{}[{}]".format(port, bit)) + + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + + return insts + def create_control_logic(self): """ Add control logic instances """ diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py index d2df2aef..575fc51f 100755 --- a/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class sram_1bank_nomux_1rw_1r_test(openram_test): +class sram_1bank_nomux_1rw_1r_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) diff --git a/compiler/tests/20_sram_1bank_nomux_wmask_spare_cols_test.py b/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py similarity index 89% rename from compiler/tests/20_sram_1bank_nomux_wmask_spare_cols_test.py rename to compiler/tests/20_sram_1bank_nomux_spare_cols_test.py index dba96ca2..14001c19 100755 --- a/compiler/tests/20_sram_1bank_nomux_wmask_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py @@ -18,14 +18,13 @@ import debug # @unittest.skip("SKIPPING 20_sram_1bank_nomux_wmask_test") -class sram_1bank_nomux_wmask_test(openram_test): +class sram_1bank_nomux_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config c = sram_config(word_size=8, - write_size=4, num_spare_cols=3, num_words=16, num_banks=1) @@ -33,13 +32,13 @@ class sram_1bank_nomux_wmask_test(openram_test): c.words_per_row = 1 c.recompute_sizes() debug.info(1, "Layout test for {}rw,{}r,{}w sram " - "with {} bit words, {} words, {} bit writes, {} words per " + "with {} bit words, {} words, {} spare cols, {} words per " "row, {} banks".format(OPTS.num_rw_ports, OPTS.num_r_ports, OPTS.num_w_ports, c.word_size, c.num_words, - c.write_size, + c.num_spare_cols, c.words_per_row, c.num_banks)) a = factory.create(module_type="sram", sram_config=c) diff --git a/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py b/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py new file mode 100755 index 00000000..902cacdd --- /dev/null +++ b/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +#@unittest.skip("SKIPPING 22_sram_1bank_2mux_sparecols_func_test") +class sram_1bank_2mux_sparecols_func_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional, delay + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=32, + num_spare_cols=3, + 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, {} spare columns, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_spare_cols, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + (fail, error) = f.run() + self.assertTrue(fail,error) + + 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(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py b/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py new file mode 100755 index 00000000..347d15d0 --- /dev/null +++ b/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +#@unittest.skip("SKIPPING 22_sram_func_test") +class sram_1bank_nomux_sparecols_func_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + OPTS.analytical_delay = False + OPTS.netlist_only = True + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=16, + num_spare_cols=3, + 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, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + (fail, error) = f.run() + self.assertTrue(fail,error) + + 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(testRunner=debugTestRunner())