diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 677aa571..b5a0f3fd 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -709,8 +709,8 @@ class layout(lef.lef): def create_channel_route(self, route_map, top_pins, bottom_pins, offset, - layer_stack=("metal1", "via1", "metal2"), pitch=None, - vertical=False): + layer_stack=("metal1", "via1", "metal2"), pitch=None, + vertical=False): """ This is a simple channel route for one-to-one connections that will jog the top route whenever there is a conflict. It does NOT @@ -749,13 +749,11 @@ class layout(lef.lef): for top_name,top_pin in top_pins.items(): for bot_name,bot_pin in bottom_pins.items(): if not vertical and abs(top_pin.center().x-bot_pin.center().x) < pitch: - # The edges only go from top to bottom - # since we will order tracks bottom up vcg[top_name].append(bot_name) + vcg[bot_name].append(top_name) elif vertical and abs(top_pin.center().y-bot_pin.center().y) < pitch: - # The edges only go from top to bottom - # since we will order tracks bottom up vcg[top_name].append(bot_name) + vcg[bot_name].append(top_name) # This is the starting offset of the first trunk if vertical: diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 460f75ce..2c639ee9 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -89,7 +89,7 @@ class delay(): self.check_arguments() # obtains list of time-points for each rising clk edge - self.obtain_cycle_times() + self.create_test_cycles() # creates and opens stimulus file for writing temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) @@ -105,25 +105,13 @@ class delay(): # generate data and addr signals self.sf.write("\n* Generation of data and address signals\n") - for i in range(self.word_size): - if i == self.probe_data: - self.gen_data(clk_times=self.cycle_times, - sig_name="DIN[{0}]".format(i)) - - - else: - self.stim.gen_constant(sig_name="DIN[{0}]".format(i), - v_val=0) - - self.gen_addr(clk_times=self.cycle_times, - addr=self.probe_address) + self.gen_data() + self.gen_addr() # generate control signals self.sf.write("\n* Generation of control signals\n") - self.gen_csb(self.cycle_times) - self.gen_web(self.cycle_times) - self.gen_oeb(self.cycle_times) + self.gen_control() self.sf.write("\n* Generation of global clock signal\n") self.stim.gen_pulse(sig_name="CLK", @@ -149,13 +137,13 @@ class delay(): self.check_arguments() # obtains list of time-points for each rising clk edge - self.obtain_cycle_times() + self.create_test_cycles() # creates and opens stimulus file for writing temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) self.sf = open(temp_stim, "w") self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period)) - self.stim = stimuli.stimuli(self.sf, self.corner) + self.stim = stimuli(self.sf, self.corner) # include UNTRIMMED files in stimulus file if trim: @@ -213,20 +201,20 @@ class delay(): targ_name=targ_name, trig_val=trig_val, targ_val=targ_val, - trig_dir="FALL", + trig_dir="RISE", targ_dir="FALL", trig_td=self.cycle_times[self.read0_cycle], - targ_td=self.cycle_times[self.read0_cycle]+0.5*self.period) + targ_td=self.cycle_times[self.read0_cycle]) self.stim.gen_meas_delay(meas_name="DELAY_LH", trig_name=trig_name, targ_name=targ_name, trig_val=trig_val, targ_val=targ_val, - trig_dir="FALL", + trig_dir="RISE", targ_dir="RISE", trig_td=self.cycle_times[self.read1_cycle], - targ_td=self.cycle_times[self.read1_cycle]+0.5*self.period) + targ_td=self.cycle_times[self.read1_cycle]) self.stim.gen_meas_delay(meas_name="SLEW_HL", trig_name=targ_name, @@ -236,7 +224,7 @@ class delay(): trig_dir="FALL", targ_dir="FALL", trig_td=self.cycle_times[self.read0_cycle], - targ_td=self.cycle_times[self.read0_cycle]+0.5*self.period) + targ_td=self.cycle_times[self.read0_cycle]) self.stim.gen_meas_delay(meas_name="SLEW_LH", trig_name=targ_name, @@ -246,7 +234,7 @@ class delay(): trig_dir="RISE", targ_dir="RISE", trig_td=self.cycle_times[self.read1_cycle], - targ_td=self.cycle_times[self.read1_cycle]+0.5*self.period) + targ_td=self.cycle_times[self.read1_cycle]) # add measure statements for power t_initial = self.cycle_times[self.write0_cycle] @@ -315,11 +303,11 @@ class delay(): feasible_delay_hl = results["delay_hl"] feasible_slew_hl = results["slew_hl"] - debug.info(1, "Found feasible_period: {0}ns feasible_delay {1}ns/{2}ns slew {3}ns/{4}ns".format(feasible_period, - feasible_delay_lh, - feasible_delay_hl, - feasible_slew_lh, - feasible_slew_hl)) + delay_str = "feasible_delay {0:.4f}ns/{1:.4f}ns".format(feasible_delay_lh, feasible_delay_hl) + slew_str = "slew {0:.4f}ns/{1:.4f}ns".format(feasible_slew_lh, feasible_slew_hl) + debug.info(1, "Found feasible_period: {0}ns {1} {2} ".format(feasible_period, + delay_str, + slew_str)) self.period = feasible_period return (feasible_delay_lh, feasible_delay_hl) @@ -392,39 +380,32 @@ class delay(): """ Check if the measurements are defined and if they are valid. """ (delay_hl, delay_lh, slew_hl, slew_lh) = delay_tuple + period_load_slew_str = "period {0} load {1} slew {2}".format(self.period,self.load, self.slew) # if it failed or the read was longer than a period if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float: - debug.info(2,"Failed simulation: period {0} load {1} slew {2}, delay_hl={3}n delay_lh={4}ns slew_hl={5}n slew_lh={6}n".format(self.period, - self.load, - self.slew, - delay_hl, - delay_lh, - slew_hl, - slew_lh)) + delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh) + slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh) + debug.info(2,"Failed simulation (in sec):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str, + delays_str, + slews_str)) return False # Scale delays to ns (they previously could have not been floats) delay_hl *= 1e9 delay_lh *= 1e9 slew_hl *= 1e9 slew_lh *= 1e9 + delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh) + slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh) if delay_hl>self.period or delay_lh>self.period or slew_hl>self.period or slew_lh>self.period: - debug.info(2,"UNsuccessful simulation: period {0} load {1} slew {2}, delay_hl={3}n delay_lh={4}ns slew_hl={5}n slew_lh={6}n".format(self.period, - self.load, - self.slew, - delay_hl, - delay_lh, - slew_hl, - slew_lh)) + debug.info(2,"UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str, + delays_str, + slews_str)) return False else: - debug.info(2,"Successful simulation: period {0} load {1} slew {2}, delay_hl={3}n delay_lh={4}ns slew_hl={5}n slew_lh={6}n".format(self.period, - self.load, - self.slew, - delay_hl, - delay_lh, - slew_hl, - slew_lh)) + debug.info(2,"Successful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str, + delays_str, + slews_str)) return True @@ -547,6 +528,8 @@ class delay(): """ Main function to characterize an SRAM for a table. Computes both delay and power characterization. """ + # Data structure for all the characterization values + char_data = {} self.set_probe(probe_address, probe_data) @@ -568,21 +551,27 @@ class delay(): debug.check(feasible_delay_lh>0,"Negative delay may not be possible") debug.check(feasible_delay_hl>0,"Negative delay may not be possible") - # 2) Measure the delay, slew and power for all slew/load pairs. + # 2) Finds the minimum period without degrading the delays by X% + self.set_load_slew(max(loads),max(slews)) + min_period = self.find_min_period(feasible_delay_lh, feasible_delay_hl) + debug.check(type(min_period)==float,"Couldn't find minimum period.") + debug.info(1, "Min Period: {0}n with a delay of {1} / {2}".format(min_period, feasible_delay_lh, feasible_delay_hl)) + char_data["min_period"] = round_time(min_period) + # Make a list for each type of measurement to append results to - char_data = {} for m in ["delay_lh", "delay_hl", "slew_lh", "slew_hl", "read0_power", "read1_power", "write0_power", "write1_power", "leakage_power"]: char_data[m]=[] - # 2a) Find the leakage power of the trimmmed and UNtrimmed arrays. + # 3) Find the leakage power of the trimmmed and UNtrimmed arrays. (full_array_leakage, trim_array_leakage)=self.run_power_simulation() char_data["leakage_power"]=full_array_leakage + # 4) At the minimum period, measure the delay, slew and power for all slew/load pairs. for slew in slews: for load in loads: self.set_load_slew(load,slew) - # 2c) Find the delay, dynamic power, and leakage power of the trimmed array. + # Find the delay, dynamic power, and leakage power of the trimmed array. (success, delay_results) = self.run_delay_simulation() debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load)) for k,v in delay_results.items(): @@ -594,113 +583,161 @@ class delay(): - # 3) Finds the minimum period without degrading the delays by X% - self.set_load_slew(max(loads),max(slews)) - min_period = self.find_min_period(feasible_delay_lh, feasible_delay_hl) - debug.check(type(min_period)==float,"Couldn't find minimum period.") - debug.info(1, "Min Period: {0}n with a delay of {1} / {2}".format(min_period, feasible_delay_lh, feasible_delay_hl)) - - # 4) Pack up the final measurements - char_data["min_period"] = round_time(min_period) - return char_data - - def obtain_cycle_times(self): + def add_data(self, data): + """ Add the array of data values """ + debug.check(len(data)==self.word_size, "Invalid data word size.") + index = 0 + for c in data: + if c=="0": + self.data_values[index].append(0) + elif c=="1": + self.data_values[index].append(1) + else: + debug.error("Non-binary data string",1) + index += 1 + + def add_address(self, address): + """ Add the array of address values """ + debug.check(len(address)==self.addr_size, "Invalid address size.") + index = 0 + for c in address: + if c=="0": + self.addr_values[index].append(0) + elif c=="1": + self.addr_values[index].append(1) + else: + debug.error("Non-binary address string",1) + index += 1 + + def add_noop(self, comment, address, data): + """ Add the control values for a read cycle. """ + self.cycle_comments.append("Cycle {0:2d}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_times), + self.t_current, + comment)) + self.cycle_times.append(self.t_current) + self.t_current += self.period + self.web_values.append(1) + self.oeb_values.append(1) + self.csb_values.append(1) + + self.add_data(data) + self.add_address(address) + + + def add_read(self, comment, address, data): + """ Add the control values for a read cycle. """ + self.cycle_comments.append("Cycle {0:2d}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments), + self.t_current, + comment)) + self.cycle_times.append(self.t_current) + self.t_current += self.period + + self.web_values.append(1) + self.oeb_values.append(0) + self.csb_values.append(0) + + self.add_data(data) + self.add_address(address) + + + + def add_write(self, comment, address, data): + """ Add the control values for a read cycle. """ + self.cycle_comments.append("Cycle {0:2d}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments), + self.t_current, + comment)) + self.cycle_times.append(self.t_current) + self.t_current += self.period + + self.web_values.append(0) + self.oeb_values.append(1) + self.csb_values.append(0) + + self.add_data(data) + self.add_address(address) + + def create_test_cycles(self): """Returns a list of key time-points [ns] of the waveform (each rising edge) of the cycles to do a timing evaluation. The last time is the end of the simulation and does not need a rising edge.""" + # Start at time 0 + self.t_current = 0 + + # Cycle times (positive edge) with comment self.cycle_comments = [] self.cycle_times = [] - t_current = 0 - # idle cycle, no operation - msg = "Idle cycle (no clock)" - self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(0, - t_current, - msg)) - self.cycle_times.append(t_current) - t_current += self.period + # Control logic signals each cycle + self.web_values = [] + self.oeb_values = [] + self.csb_values = [] - # One period - msg = "W data 1 address 11..11 to initialize cell" - self.cycle_times.append(t_current) - self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, - t_current, - msg)) - t_current += self.period + # Address and data values for each address/data bit + self.data_values=[] + for i in range(self.word_size): + self.data_values.append([]) + self.addr_values=[] + for i in range(self.addr_size): + self.addr_values.append([]) - # One period - msg = "W data 0 address 11..11 (to ensure a write of value works)" - self.cycle_times.append(t_current) - self.write0_cycle=len(self.cycle_times)-1 - self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, - t_current, - msg)) - t_current += self.period + # Create the inverse address for a scratch address + inverse_address = "" + for c in self.probe_address: + if c=="0": + inverse_address += "1" + elif c=="1": + inverse_address += "0" + else: + debug.error("Non-binary address string",1) + + # For now, ignore data patterns and write ones or zeros + data_ones = "1"*self.word_size + data_zeros = "0"*self.word_size - # One period - msg = "W data 1 address 00..00 (to clear bus caps)" - self.cycle_times.append(t_current) - self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, - t_current, - msg)) - t_current += self.period + self.add_noop("Idle cycle (no positive clock edge)", + inverse_address, data_zeros) - # One period - msg = "R data 0 address 11..11 to check W0 worked" - self.cycle_times.append(t_current) - self.read0_cycle=len(self.cycle_times)-1 - self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, - t_current, - msg)) - t_current += self.period + self.add_write("W data 1 address 0..00", + inverse_address,data_ones) - # One period - msg = "Idle cycle (Read addr 00..00)" - self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, - t_current, - msg)) - self.cycle_times.append(t_current) - self.idle_cycle=len(self.cycle_times)-1 - t_current += self.period - - # One period - msg = "W data 1 address 11..11 (to ensure a write of value worked)" - self.cycle_times.append(t_current) - self.write1_cycle=len(self.cycle_times)-1 - self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, - t_current, - msg)) - t_current += self.period - - # One period - msg = "W data 0 address 00..00 (to clear bus caps)" - self.cycle_times.append(t_current) - self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, - t_current, - msg)) - t_current += self.period + self.add_write("W data 0 address 11..11 to write value", + self.probe_address,data_zeros) + self.write0_cycle=len(self.cycle_times)-1 # Remember for power measure - # One period - msg = "R data 1 address 11..11 to check W1 worked" - self.cycle_times.append(t_current) - self.read1_cycle=len(self.cycle_times)-1 - self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, - t_current, - msg)) - t_current += self.period + # This also ensures we will have a H->L transition on the next read + self.add_read("R data 1 address 00..00 to set DOUT caps", + inverse_address,data_zeros) - # One period - msg = "Idle cycle (Read addr 11..11)" - self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, - t_current, - msg)) - self.cycle_times.append(t_current) - t_current += self.period + self.add_read("R data 0 address 11..11 to check W0 worked", + self.probe_address,data_zeros) + self.read0_cycle=len(self.cycle_times)-1 # Remember for power measure + + self.add_noop("Idle cycle (if read takes >1 cycle)", + inverse_address,data_zeros) + self.idle_cycle=len(self.cycle_times)-1 # Remember for power measure + + self.add_write("W data 1 address 11..11 to write value", + self.probe_address,data_ones) + self.write1_cycle=len(self.cycle_times)-1 # Remember for power measure + + self.add_write("W data 0 address 00..00 to clear DIN caps", + inverse_address,data_zeros) + + # This also ensures we will have a L->H transition on the next read + self.add_read("R data 0 address 00..00 to clear DOUT caps", + inverse_address,data_zeros) + + self.add_read("R data 1 address 11..11 to check W1 worked", + self.probe_address,data_zeros) + self.read1_cycle=len(self.cycle_times)-1 # Remember for power measure + + self.add_noop("Idle cycle (if read takes >1 cycle))", + self.probe_address,data_zeros) @@ -741,48 +778,24 @@ class delay(): } return data - def gen_data(self, clk_times, sig_name): + def gen_data(self): """ Generates the PWL data inputs for a simulation timing test. """ - # values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP - # we are asserting the opposite value on the other side of the tx gate during - # the read to be "worst case". Otherwise, it can actually assist the read. - values = [0, 1, 0, 1, 1, 1, 1, 0, 0, 0 ] - self.stim.gen_pwl(sig_name, clk_times, values, self.period, self.slew, 0.05) + for i in range(self.word_size): + sig_name="DIN[{0}]".format(i) + self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[i], self.period, self.slew, 0.05) - def gen_addr(self, clk_times, addr): + def gen_addr(self): """ Generates the address inputs for a simulation timing test. This alternates between all 1's and all 0's for the address. """ - - zero_values = [0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ] - ones_values = [1, 1, 1, 0, 1, 0, 1, 0, 1, 1 ] - - for i in range(len(addr)): + for i in range(self.addr_size): sig_name = "A[{0}]".format(i) - if addr[i]=="1": - self.stim.gen_pwl(sig_name, clk_times, ones_values, self.period, self.slew, 0.05) - else: - self.stim.gen_pwl(sig_name, clk_times, zero_values, self.period, self.slew, 0.05) + self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[i], self.period, self.slew, 0.05) - def gen_csb(self, clk_times): - """ Generates the PWL CSb signal """ - # values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP - # Keep CSb asserted in NOP for measuring >1 period - values = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] - self.stim.gen_pwl("csb", clk_times, values, self.period, self.slew, 0.05) - - def gen_web(self, clk_times): - """ Generates the PWL WEb signal """ - # values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP - # Keep WEb deasserted in NOP for measuring >1 period - values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1] - self.stim.gen_pwl("web", clk_times, values, self.period, self.slew, 0.05) - - def gen_oeb(self, clk_times): - """ Generates the PWL WEb signal """ - # values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP - # Keep OEb asserted in NOP for measuring >1 period - values = [1, 1, 1, 1, 0, 0, 1, 1, 0, 0] - self.stim.gen_pwl("oeb", clk_times, values, self.period, self.slew, 0.05) + def gen_control(self): + """ Generates the control signals """ + self.stim.gen_pwl("csb", self.cycle_times, self.csb_values, self.period, self.slew, 0.05) + self.stim.gen_pwl("web", self.cycle_times, self.web_values, self.period, self.slew, 0.05) + self.stim.gen_pwl("oeb", self.cycle_times, self.oeb_values, self.period, self.slew, 0.05) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 416ab0d8..2ac65513 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -300,28 +300,34 @@ class lib: def write_data_bus(self): """ Adds data bus timing results.""" - self.lib.write(" bus(DATA){\n") + self.lib.write(" bus(DIN){\n") self.lib.write(" bus_type : DATA; \n") - self.lib.write(" direction : inout; \n") + self.lib.write(" direction : in; \n") # This is conservative, but limit to range that we characterized. self.lib.write(" max_capacitance : {0}; \n".format(max(self.loads))) self.lib.write(" min_capacitance : {0}; \n".format(min(self.loads))) - self.lib.write(" three_state : \"!OEb & !clk\"; \n") self.lib.write(" memory_write(){ \n") self.lib.write(" address : ADDR; \n") self.lib.write(" clocked_on : clk; \n") self.lib.write(" }\n") + + self.lib.write(" bus(DOUT){\n") + self.lib.write(" bus_type : DATA; \n") + self.lib.write(" direction : out; \n") + # This is conservative, but limit to range that we characterized. + self.lib.write(" max_capacitance : {0}; \n".format(max(self.loads))) + self.lib.write(" min_capacitance : {0}; \n".format(min(self.loads))) self.lib.write(" memory_read(){ \n") self.lib.write(" address : ADDR; \n") self.lib.write(" }\n") + - - self.lib.write(" pin(DATA[{0}:0]){{\n".format(self.sram.word_size - 1)) + self.lib.write(" pin(DOUT[{0}:0]){{\n".format(self.sram.word_size - 1)) self.write_FF_setuphold() self.lib.write(" timing(){ \n") self.lib.write(" timing_sense : non_unate; \n") self.lib.write(" related_pin : \"clk\"; \n") - self.lib.write(" timing_type : falling_edge; \n") + self.lib.write(" timing_type : rising_edge; \n") self.lib.write(" cell_rise(CELL_TABLE) {\n") self.write_values(self.char_results["delay_lh"],len(self.loads)," ") self.lib.write(" }\n") # rise delay @@ -374,7 +380,7 @@ class lib: self.lib.write(" pin(clk){\n") self.lib.write(" clock : true;\n") self.lib.write(" direction : input; \n") - # This should actually be a min inverter cap, but ok... + # FIXME: This depends on the clock buffer size in the control logic self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"])) # Find the average power of 1 and 0 bits for writes and reads over all loads/slews diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 0c939a1f..2cca1384 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -144,7 +144,7 @@ class stimuli(): times = np.array(clk_times) - setup*period values = np.array(data_values) * self.voltage half_slew = 0.5 * slew - self.sf.write("* (time, data): {}\n".format(zip(clk_times, data_values))) + self.sf.write("* (time, data): {}\n".format(list(zip(clk_times, data_values)))) self.sf.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0])) for i in range(1,len(times)): self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew, diff --git a/compiler/characterizer/trim_spice.py b/compiler/characterizer/trim_spice.py index d3be0dc8..c3a6ee69 100644 --- a/compiler/characterizer/trim_spice.py +++ b/compiler/characterizer/trim_spice.py @@ -55,7 +55,7 @@ class trim_spice(): col_address = 0 # 1. Keep cells in the bitcell array based on WL and BL wl_name = "wl[{}]".format(wl_address) - bl_name = "bl[{}]".format(self.words_per_row*data_bit + col_address) + bl_name = "bl[{}]".format(int(self.words_per_row*data_bit + col_address)) # Prepend info about the trimming addr_msg = "Keeping {} address".format(address) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index d6a7998d..99018a9a 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -61,7 +61,9 @@ class control_logic(design.design): self.add_mod(self.nand3) # Special gates: inverters for buffering - self.clkbuf = pinvbuf(4,16,height=dff_height) + # 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.add_mod(self.clkbuf) self.inv = self.inv1 = pinv(size=1, height=dff_height) self.add_mod(self.inv1) @@ -345,7 +347,7 @@ class control_logic(design.design): x_off = self.ctrl_dff_inst.width + self.internal_bus_width (y_off,mirror)=self.get_offset(row) - # input: WE, clk_buf_bar, CS output: w_en_bar + # input: WE, CS output: w_en_bar w_en_bar_offset = vector(x_off, y_off) self.w_en_bar_inst=self.add_inst(name="nand3_w_en_bar", mod=self.nand3, diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index e7b45d05..34d9b7f3 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -20,6 +20,12 @@ class dff_buf(design.design): design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) + # This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width. + # This causes a DRC in the pinv which assumes min width rails. This ensures the output + # contact does not violate spacing to the rail in the NMOS. + debug.check(inv1_size>=2, "Inverter must be greater than two for rail spacing DRC rules.") + debug.check(inv2_size>=2, "Inverter must be greater than two for rail spacing DRC rules.") + from importlib import reload c = reload(__import__(OPTS.dff)) self.mod_dff = getattr(c, OPTS.dff) diff --git a/compiler/modules/dff_inv.py b/compiler/modules/dff_inv.py index f29a83ce..cb5b0478 100644 --- a/compiler/modules/dff_inv.py +++ b/compiler/modules/dff_inv.py @@ -12,19 +12,24 @@ class dff_inv(design.design): do not have Qbar, so this will create it. """ - def __init__(self, inv_size=1, name=""): + def __init__(self, inv_size=2, name=""): if name=="": name = "dff_inv_{0}".format(inv_size) design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) + # This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width. + # This causes a DRC in the pinv which assumes min width rails. This ensures the output + # contact does not violate spacing to the rail in the NMOS. + debug.check(inv_size>=2, "Inverter must be greater than two for rail spacing DRC rules.") + from importlib import reload c = reload(__import__(OPTS.dff)) self.mod_dff = getattr(c, OPTS.dff) self.dff = self.mod_dff("dff") self.add_mod(self.dff) - + self.inv1 = pinv(size=inv_size,height=self.dff.height) self.add_mod(self.inv1) diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 8a41e147..2ddda761 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -127,14 +127,14 @@ class wordline_driver(design.design): mirror=inst_mirror)) self.connect_inst(["en_bar[{0}]".format(row), "in[{0}]".format(row), - "net[{0}]".format(row), + "wl_bar[{0}]".format(row), "vdd", "gnd"]) # add inv2 self.inv2_inst.append(self.add_inst(name=name_inv2, mod=self.inv, offset=inv2_offset, mirror=inst_mirror)) - self.connect_inst(["net[{0}]".format(row), + self.connect_inst(["wl_bar[{0}]".format(row), "wl[{0}]".format(row), "vdd", "gnd"]) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 5328c539..48644b7a 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -96,7 +96,9 @@ class pinv(pgate.pgate): nmos_height_available = 0.5 * tx_height_available - 0.5*drc["poly_to_poly"] pmos_height_available = 0.5 * tx_height_available - 0.5*drc["poly_to_poly"] - debug.info(2,"Height avail {0} PMOS height {1} NMOS height {2}".format(tx_height_available, nmos_height_available, pmos_height_available)) + debug.info(2,"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available, + nmos_height_available, + pmos_height_available)) # Determine the number of mults for each to fit width into available space self.nmos_width = self.nmos_size*drc["minwidth_tx"] diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index 94ec2a6f..6347c157 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -15,20 +15,35 @@ class pinvbuf(design.design): c = reload(__import__(OPTS.bitcell)) bitcell = getattr(c, OPTS.bitcell) - def __init__(self, inv1_size=2, inv2_size=4, height=bitcell.height, name=""): + unique_id = 1 + + def __init__(self, driver_size=4, height=bitcell.height, name=""): + + stage_effort = 4 + # FIXME: Change the number of stages to support high drives. + + # stage effort of 4 or less + # The pinvbuf has a FO of 2 for the first stage, so the second stage + # should be sized "half" to prevent loading of the first stage + predriver_size = max(int(driver_size/(stage_effort/2)),1) if name=="": - name = "pinvbuf_{0}_{1}".format(inv1_size, inv2_size) + name = "pinvbuf_{0}_{1}_{2}".format(predriver_size, driver_size, pinvbuf.unique_id) + pinvbuf.unique_id += 1 + design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) - self.inv = pinv(size=1, height=height) + + # Shield the cap, but have at least a stage effort of 4 + input_size = max(1,int(predriver_size/stage_effort)) + self.inv = pinv(size=input_size, height=height) self.add_mod(self.inv) - self.inv1 = pinv(size=inv1_size, height=height) + self.inv1 = pinv(size=predriver_size, height=height) self.add_mod(self.inv1) - self.inv2 = pinv(size=inv2_size, height=height) + self.inv2 = pinv(size=driver_size, height=height) self.add_mod(self.inv2) self.width = 2*self.inv1.width + self.inv2.width diff --git a/compiler/tests/04_pinvbuf_test.py b/compiler/tests/04_pinvbuf_test.py index ffe6fa33..3b16c1c9 100755 --- a/compiler/tests/04_pinvbuf_test.py +++ b/compiler/tests/04_pinvbuf_test.py @@ -21,7 +21,7 @@ class pinvbuf_test(openram_test): import pinvbuf debug.info(2, "Testing inverter/buffer 4x 8x") - a = pinvbuf.pinvbuf(4,8) + a = pinvbuf.pinvbuf(8) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index 2b89d3ac..0d256c5b 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -41,7 +41,7 @@ class timing_sram_test(openram_test): probe_address = "1" * s.s.addr_size probe_data = s.s.word_size - 1 - debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) + debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data)) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) d = delay(s.s, tempspice, corner) @@ -49,41 +49,36 @@ class timing_sram_test(openram_test): loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] data = d.analyze(probe_address, probe_data, slews, loads) - #print data + if OPTS.tech_name == "freepdk45": - golden_data = {'leakage_power': 0.0006964536000000001, - 'delay_lh': [0.0573055], - 'read0_power': [0.0337812], - 'read1_power': [0.032946500000000004], - 'write1_power': [0.0361529], - 'write0_power': [0.026179099999999997], - 'slew_hl': [0.0285185], - 'min_period': 0.205, - 'delay_hl': [0.070554], - 'slew_lh': [0.0190073]} + golden_data = {'delay_hl': [2.5614], + 'delay_lh': [0.22929839999999999], + 'leakage_power': 0.0020326, + 'min_period': 4.844, + 'read0_power': [0.0497676], + 'read1_power': [0.0463576], + 'slew_hl': [0.1119293], + 'slew_lh': [0.0237043], + 'write0_power': [0.0494321], + 'write1_power': [0.0457268]} elif OPTS.tech_name == "scn3me_subm": - golden_data = {'leakage_power': 0.0004004581, - 'delay_lh': [0.6538954], - 'read0_power': [9.7622], - 'read1_power': [9.589], - 'write1_power': [10.8], - 'write0_power': [6.928400000000001], - 'slew_hl': [0.8321625], - 'min_period': 2.344, - 'delay_hl': [0.9019090999999999], - 'slew_lh': [0.5896232]} + golden_data = {'delay_hl': [6.0052], + 'delay_lh': [2.2886], + 'leakage_power': 0.025629199999999998, + 'min_period': 9.375, + 'read0_power': [8.8721], + 'read1_power': [8.3179], + 'slew_hl': [1.0746], + 'slew_lh': [0.413426], + 'write0_power': [8.6601], + 'write1_power': [8.0397]} else: self.assertTrue(False) # other techs fail # Check if no too many or too few results self.assertTrue(len(data.keys())==len(golden_data.keys())) - # Check each result - for k in data.keys(): - if type(data[k])==list: - for i in range(len(data[k])): - self.isclose(data[k][i],golden_data[k][i],0.15) - else: - self.isclose(data[k],golden_data[k],0.15) + self.assertTrue(self.check_golden_data(data,golden_data,0.25)) + globals.end_openram() # instantiate a copdsay of the class to actually run the test diff --git a/compiler/tests/21_hspice_setuphold_test.py b/compiler/tests/21_hspice_setuphold_test.py index f9d8a01c..b123ed57 100755 --- a/compiler/tests/21_hspice_setuphold_test.py +++ b/compiler/tests/21_hspice_setuphold_test.py @@ -49,13 +49,8 @@ class timing_setup_test(openram_test): # Check if no too many or too few results self.assertTrue(len(data.keys())==len(golden_data.keys())) - # Check each result - for k in data.keys(): - if type(data[k])==list: - for i in range(len(data[k])): - self.isclose(data[k][i],golden_data[k][i],0.15) - else: - self.isclose(data[k],golden_data[k],0.15) + + self.assertTrue(self.check_golden_data(data,golden_data,0.25)) globals.end_openram() diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index d10b9e61..72bdbc65 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -17,6 +17,7 @@ class timing_sram_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) OPTS.spice_name="ngspice" OPTS.analytical_delay = False + OPTS.trim_netlist = False # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload @@ -39,7 +40,7 @@ class timing_sram_test(openram_test): probe_address = "1" * s.s.addr_size probe_data = s.s.word_size - 1 - debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) + debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data)) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) d = delay(s.s, tempspice, corner) @@ -47,41 +48,36 @@ class timing_sram_test(openram_test): loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] data = d.analyze(probe_address, probe_data, slews, loads) - #print data + if OPTS.tech_name == "freepdk45": - golden_data = {'leakage_power': 0.0007348262, - 'delay_lh': [0.05799613], - 'read0_power': [0.0384102], - 'read1_power': [0.03279848], - 'write1_power': [0.03693655], - 'write0_power': [0.02717752], - 'slew_hl': [0.03607912], - 'min_period': 0.742, - 'delay_hl': [0.3929995], - 'slew_lh': [0.02160862]} + golden_data = {'delay_hl': [2.562671], + 'delay_lh': [0.2320771], + 'leakage_power': 0.00102373, + 'min_period': 4.844, + 'read0_power': [0.047404110000000006], + 'read1_power': [0.0438884], + 'slew_hl': [0.1140206], + 'slew_lh': [0.02492785], + 'write0_power': [0.04765188], + 'write1_power': [0.04434999]} elif OPTS.tech_name == "scn3me_subm": - golden_data = {'leakage_power': 0.00142014, - 'delay_lh': [0.8018421], - 'read0_power': [11.44908], - 'read1_power': [11.416549999999999], - 'write1_power': [11.718020000000001], - 'write0_power': [8.250219], - 'slew_hl': [0.8273725], - 'min_period': 34, - 'delay_hl': [1.085861], - 'slew_lh': [0.5730144]} + golden_data = {'delay_hl': [11.69536], + 'delay_lh': [1.260921], + 'leakage_power': 0.00039469710000000004, + 'min_period': 20.0, + 'read0_power': [4.40238], + 'read1_power': [4.126633], + 'slew_hl': [1.259555], + 'slew_lh': [0.9150649], + 'write0_power': [4.988347], + 'write1_power': [4.473887]} else: self.assertTrue(False) # other techs fail # Check if no too many or too few results self.assertTrue(len(data.keys())==len(golden_data.keys())) - # Check each result - for k in data.keys(): - if type(data[k])==list: - for i in range(len(data[k])): - self.isclose(data[k][i],golden_data[k][i],0.15) - else: - self.isclose(data[k],golden_data[k],0.15) + + self.assertTrue(self.check_golden_data(data,golden_data,0.25)) globals.end_openram() diff --git a/compiler/tests/21_ngspice_setuphold_test.py b/compiler/tests/21_ngspice_setuphold_test.py index 24c69aa7..849a23f0 100755 --- a/compiler/tests/21_ngspice_setuphold_test.py +++ b/compiler/tests/21_ngspice_setuphold_test.py @@ -49,14 +49,9 @@ class timing_setup_test(openram_test): # Check if no too many or too few results self.assertTrue(len(data.keys())==len(golden_data.keys())) - # Check each result - for k in data.keys(): - if type(data[k])==list: - for i in range(len(data[k])): - self.isclose(data[k][i],golden_data[k][i],0.15) - else: - self.isclose(data[k],golden_data[k],0.15) + self.assertTrue(self.check_golden_data(data,golden_data,0.25)) + reload(characterizer) globals.end_openram() diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py index a41460f7..5d1d641c 100755 --- a/compiler/tests/23_lib_sram_model_test.py +++ b/compiler/tests/23_lib_sram_model_test.py @@ -40,7 +40,7 @@ class lib_test(openram_test): newname = filename.replace(".lib","_analytical.lib") libname = "{0}/{1}".format(OPTS.openram_temp,filename) golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname) - self.isapproxdiff(libname,golden,0.15) + self.assertTrue(self.isapproxdiff(libname,golden,0.15)) globals.end_openram() diff --git a/compiler/tests/23_lib_sram_prune_test.py b/compiler/tests/23_lib_sram_prune_test.py index 4a49bc74..35de23aa 100755 --- a/compiler/tests/23_lib_sram_prune_test.py +++ b/compiler/tests/23_lib_sram_prune_test.py @@ -49,7 +49,7 @@ class lib_test(openram_test): newname = filename.replace(".lib","_pruned.lib") libname = "{0}/{1}".format(OPTS.openram_temp,filename) golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname) - self.isapproxdiff(libname,golden,0.40) + self.assertTrue(self.isapproxdiff(libname,golden,0.40)) reload(characterizer) globals.end_openram() diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py index 9338b6c4..8dc5c7a0 100755 --- a/compiler/tests/23_lib_sram_test.py +++ b/compiler/tests/23_lib_sram_test.py @@ -48,7 +48,7 @@ class lib_test(openram_test): for filename in lib_files: libname = "{0}/{1}".format(OPTS.openram_temp,filename) golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename) - self.isapproxdiff(libname,golden,0.40) + self.assertTrue(self.isapproxdiff(libname,golden,0.40)) reload(characterizer) globals.end_openram() diff --git a/compiler/tests/30_openram_test.py b/compiler/tests/30_openram_test.py index 51546ae3..81864840 100755 --- a/compiler/tests/30_openram_test.py +++ b/compiler/tests/30_openram_test.py @@ -64,10 +64,13 @@ class openram_test(openram_test): self.assertTrue(len(files)>0) # grep any errors from the output - output = open("{0}/output.log".format(out_path),"r").read() + output_log = open("{0}/output.log".format(out_path),"r") + output = output_log.read() + output_log.close() self.assertEqual(len(re.findall('ERROR',output)),0) self.assertEqual(len(re.findall('WARNING',output)),0) + # now clean up the directory if os.path.exists(out_path): shutil.rmtree(out_path, ignore_errors=True) diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C.lib index d65b5ab0..cf9df15f 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C.lib @@ -78,27 +78,31 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 1032.3999375; + area : 948.52275; leakage_power () { when : "CSb"; - value : 0.0008128352; + value : 0.0021292; } cell_leakage_power : 0; - bus(DATA){ + bus(DIN){ bus_type : DATA; - direction : inout; + direction : in; max_capacitance : 1.6728; min_capacitance : 0.052275; - three_state : "!OEb & !clk"; memory_write(){ address : ADDR; clocked_on : clk; } + bus(DOUT){ + bus_type : DATA; + direction : out; + max_capacitance : 1.6728; + min_capacitance : 0.052275; memory_read(){ address : ADDR; } - pin(DATA[1:0]){ + pin(DOUT[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk"; @@ -130,26 +134,26 @@ cell (sram_2_16_1_freepdk45){ timing(){ timing_sense : non_unate; related_pin : "clk"; - timing_type : falling_edge; + timing_type : rising_edge; cell_rise(CELL_TABLE) { - values("0.055, 0.056, 0.063",\ - "0.056, 0.057, 0.063",\ - "0.061, 0.062, 0.069"); + values("0.229, 0.23, 0.234",\ + "0.23, 0.23, 0.234",\ + "0.236, 0.236, 0.24"); } cell_fall(CELL_TABLE) { - values("0.067, 0.068, 0.076",\ - "0.067, 0.068, 0.077",\ - "0.073, 0.074, 0.082"); + values("2.555, 2.556, 2.568",\ + "2.555, 2.557, 2.569",\ + "2.562, 2.563, 2.575"); } rise_transition(CELL_TABLE) { - values("0.013, 0.015, 0.026",\ - "0.013, 0.015, 0.026",\ - "0.014, 0.015, 0.026"); + values("0.02, 0.021, 0.028",\ + "0.02, 0.021, 0.028",\ + "0.02, 0.021, 0.028"); } fall_transition(CELL_TABLE) { - values("0.023, 0.024, 0.037",\ - "0.023, 0.024, 0.037",\ - "0.024, 0.024, 0.037"); + values("0.111, 0.112, 0.115",\ + "0.111, 0.111, 0.115",\ + "0.111, 0.111, 0.116"); } } } @@ -298,19 +302,19 @@ cell (sram_2_16_1_freepdk45){ internal_power(){ when : "!CSb & clk & !WEb"; rise_power(scalar){ - values("0.0175059861111"); + values("0.027431397222222223"); } fall_power(scalar){ - values("0.0175059861111"); + values("0.027431397222222223"); } } internal_power(){ when : "!CSb & !clk & WEb"; rise_power(scalar){ - values("0.0218644166667"); + values("0.026240397222222222"); } fall_power(scalar){ - values("0.0218644166667"); + values("0.026240397222222222"); } } internal_power(){ @@ -326,20 +330,20 @@ cell (sram_2_16_1_freepdk45){ timing_type :"min_pulse_width"; related_pin : clk; rise_constraint(scalar) { - values("0.117"); + values("2.422"); } fall_constraint(scalar) { - values("0.117"); + values("2.422"); } } timing(){ timing_type :"minimum_period"; related_pin : clk; rise_constraint(scalar) { - values("0.234"); + values("4.844"); } fall_constraint(scalar) { - values("0.234"); + values("4.844"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_analytical.lib index 5143e33a..c14afc35 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_analytical.lib @@ -78,27 +78,31 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 1032.3999375; + area : 948.52275; leakage_power () { when : "CSb"; - value : 0.000173; + value : 0.000168; } cell_leakage_power : 0; - bus(DATA){ + bus(DIN){ bus_type : DATA; - direction : inout; + direction : in; max_capacitance : 1.6728; min_capacitance : 0.052275; - three_state : "!OEb & !clk"; memory_write(){ address : ADDR; clocked_on : clk; } + bus(DOUT){ + bus_type : DATA; + direction : out; + max_capacitance : 1.6728; + min_capacitance : 0.052275; memory_read(){ address : ADDR; } - pin(DATA[1:0]){ + pin(DOUT[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk"; @@ -130,16 +134,16 @@ cell (sram_2_16_1_freepdk45){ timing(){ timing_sense : non_unate; related_pin : "clk"; - timing_type : falling_edge; + timing_type : rising_edge; cell_rise(CELL_TABLE) { - values("0.123, 0.124, 0.133",\ - "0.123, 0.124, 0.133",\ - "0.123, 0.124, 0.133"); + values("0.103, 0.104, 0.113",\ + "0.103, 0.104, 0.113",\ + "0.103, 0.104, 0.113"); } cell_fall(CELL_TABLE) { - values("0.123, 0.124, 0.133",\ - "0.123, 0.124, 0.133",\ - "0.123, 0.124, 0.133"); + values("0.103, 0.104, 0.113",\ + "0.103, 0.104, 0.113",\ + "0.103, 0.104, 0.113"); } rise_transition(CELL_TABLE) { values("0.006, 0.007, 0.018",\ @@ -298,19 +302,19 @@ cell (sram_2_16_1_freepdk45){ internal_power(){ when : "!CSb & clk & !WEb"; rise_power(scalar){ - values("0.065526962224"); + values("0.0739870044551111"); } fall_power(scalar){ - values("0.065526962224"); + values("0.0739870044551111"); } } internal_power(){ when : "!CSb & !clk & WEb"; rise_power(scalar){ - values("0.065526962224"); + values("0.0739870044551111"); } fall_power(scalar){ - values("0.065526962224"); + values("0.0739870044551111"); } } internal_power(){ @@ -336,10 +340,10 @@ cell (sram_2_16_1_freepdk45){ timing_type :"minimum_period"; related_pin : clk; rise_constraint(scalar) { - values("0.0"); + values("0"); } fall_constraint(scalar) { - values("0.0"); + values("0"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_pruned.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_pruned.lib index c856cf58..1cd10b44 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_pruned.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_pruned.lib @@ -78,27 +78,31 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 1032.3999375; + area : 948.52275; leakage_power () { when : "CSb"; - value : 0.0008128352; + value : 0.0021292; } cell_leakage_power : 0; - bus(DATA){ + bus(DIN){ bus_type : DATA; - direction : inout; + direction : in; max_capacitance : 1.6728; min_capacitance : 0.052275; - three_state : "!OEb & !clk"; memory_write(){ address : ADDR; clocked_on : clk; } + bus(DOUT){ + bus_type : DATA; + direction : out; + max_capacitance : 1.6728; + min_capacitance : 0.052275; memory_read(){ address : ADDR; } - pin(DATA[1:0]){ + pin(DOUT[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk"; @@ -130,26 +134,26 @@ cell (sram_2_16_1_freepdk45){ timing(){ timing_sense : non_unate; related_pin : "clk"; - timing_type : falling_edge; + timing_type : rising_edge; cell_rise(CELL_TABLE) { - values("0.054, 0.055, 0.061",\ - "0.055, 0.056, 0.062",\ - "0.06, 0.061, 0.068"); + values("0.227, 0.227, 0.231",\ + "0.227, 0.228, 0.232",\ + "0.233, 0.234, 0.238"); } cell_fall(CELL_TABLE) { - values("0.066, 0.067, 0.075",\ - "0.067, 0.068, 0.076",\ - "0.072, 0.073, 0.082"); + values("2.555, 2.557, 2.569",\ + "2.556, 2.557, 2.569",\ + "2.562, 2.563, 2.576"); } rise_transition(CELL_TABLE) { - values("0.013, 0.014, 0.026",\ - "0.013, 0.015, 0.026",\ - "0.013, 0.015, 0.026"); + values("0.02, 0.021, 0.028",\ + "0.02, 0.021, 0.028",\ + "0.02, 0.021, 0.028"); } fall_transition(CELL_TABLE) { - values("0.023, 0.024, 0.037",\ - "0.023, 0.024, 0.037",\ - "0.024, 0.024, 0.037"); + values("0.11, 0.11, 0.114",\ + "0.109, 0.11, 0.113",\ + "0.11, 0.11, 0.114"); } } } @@ -298,19 +302,19 @@ cell (sram_2_16_1_freepdk45){ internal_power(){ when : "!CSb & clk & !WEb"; rise_power(scalar){ - values("0.0159801855389"); + values("0.025181683333333333"); } fall_power(scalar){ - values("0.0159801855389"); + values("0.025181683333333333"); } } internal_power(){ when : "!CSb & !clk & WEb"; rise_power(scalar){ - values("0.0171325605389"); + values("0.024945991666666667"); } fall_power(scalar){ - values("0.0171325605389"); + values("0.024945991666666667"); } } internal_power(){ @@ -326,20 +330,20 @@ cell (sram_2_16_1_freepdk45){ timing_type :"min_pulse_width"; related_pin : clk; rise_constraint(scalar) { - values("0.1125"); + values("2.422"); } fall_constraint(scalar) { - values("0.1125"); + values("2.422"); } } timing(){ timing_type :"minimum_period"; related_pin : clk; rise_constraint(scalar) { - values("0.225"); + values("4.844"); } fall_constraint(scalar) { - values("0.225"); + values("4.844"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C.lib b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C.lib index 81133459..f43c12c7 100644 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C.lib +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C.lib @@ -78,27 +78,31 @@ cell (sram_2_16_1_scn3me_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 134589.78; + area : 142800.38999999998; leakage_power () { when : "CSb"; - value : 0.0004764706; + value : 0.0252988; } cell_leakage_power : 0; - bus(DATA){ + bus(DIN){ bus_type : DATA; - direction : inout; + direction : in; max_capacitance : 78.5936; min_capacitance : 2.45605; - three_state : "!OEb & !clk"; memory_write(){ address : ADDR; clocked_on : clk; } + bus(DOUT){ + bus_type : DATA; + direction : out; + max_capacitance : 78.5936; + min_capacitance : 2.45605; memory_read(){ address : ADDR; } - pin(DATA[1:0]){ + pin(DOUT[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk"; @@ -130,26 +134,26 @@ cell (sram_2_16_1_scn3me_subm){ timing(){ timing_sense : non_unate; related_pin : "clk"; - timing_type : falling_edge; + timing_type : rising_edge; cell_rise(CELL_TABLE) { - values("0.474, 0.52, 0.888",\ - "0.477, 0.522, 0.892",\ - "0.517, 0.561, 0.929"); + values("0.945, 0.976, 1.139",\ + "0.948, 0.98, 1.143",\ + "1.003, 1.036, 1.202"); } cell_fall(CELL_TABLE) { - values("0.582, 0.658, 1.26",\ - "0.586, 0.661, 1.262",\ - "0.626, 0.7, 1.298"); + values("11.211, 11.266, 11.754",\ + "11.212, 11.267, 11.755",\ + "11.264, 11.319, 11.806"); } rise_transition(CELL_TABLE) { - values("0.155, 0.233, 1.087",\ - "0.156, 0.235, 1.086",\ - "0.16, 0.239, 1.086"); + values("0.605, 0.629, 0.98",\ + "0.605, 0.629, 0.979",\ + "0.604, 0.628, 0.973"); } fall_transition(CELL_TABLE) { - values("0.277, 0.356, 1.502",\ - "0.278, 0.358, 1.501",\ - "0.279, 0.363, 1.5"); + values("11.17, 11.175, 1.284",\ + "11.167, 11.173, 1.284",\ + "11.173, 11.179, 11.473"); } } } @@ -298,19 +302,19 @@ cell (sram_2_16_1_scn3me_subm){ internal_power(){ when : "!CSb & clk & !WEb"; rise_power(scalar){ - values("4.92665"); + values("2.1762222222222225"); } fall_power(scalar){ - values("4.92665"); + values("2.1762222222222225"); } } internal_power(){ when : "!CSb & !clk & WEb"; rise_power(scalar){ - values("5.74515833333"); + values("2.167955555555556"); } fall_power(scalar){ - values("5.74515833333"); + values("2.167955555555556"); } } internal_power(){ @@ -326,20 +330,20 @@ cell (sram_2_16_1_scn3me_subm){ timing_type :"min_pulse_width"; related_pin : clk; rise_constraint(scalar) { - values("1.875"); + values("9.6875"); } fall_constraint(scalar) { - values("1.875"); + values("9.6875"); } } timing(){ timing_type :"minimum_period"; related_pin : clk; rise_constraint(scalar) { - values("3.75"); + values("19.375"); } fall_constraint(scalar) { - values("3.75"); + values("19.375"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_analytical.lib index 789f61a4..6e6c9501 100644 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_analytical.lib @@ -78,27 +78,31 @@ cell (sram_2_16_1_scn3me_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 134589.78; + area : 142800.38999999998; leakage_power () { when : "CSb"; - value : 0.000173; + value : 0.000168; } cell_leakage_power : 0; - bus(DATA){ + bus(DIN){ bus_type : DATA; - direction : inout; + direction : in; max_capacitance : 78.5936; min_capacitance : 2.45605; - three_state : "!OEb & !clk"; memory_write(){ address : ADDR; clocked_on : clk; } + bus(DOUT){ + bus_type : DATA; + direction : out; + max_capacitance : 78.5936; + min_capacitance : 2.45605; memory_read(){ address : ADDR; } - pin(DATA[1:0]){ + pin(DOUT[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk"; @@ -130,16 +134,16 @@ cell (sram_2_16_1_scn3me_subm){ timing(){ timing_sense : non_unate; related_pin : "clk"; - timing_type : falling_edge; + timing_type : rising_edge; cell_rise(CELL_TABLE) { - values("0.556, 0.603, 1.044",\ - "0.556, 0.603, 1.044",\ - "0.556, 0.603, 1.044"); + values("0.54, 0.587, 1.028",\ + "0.54, 0.587, 1.028",\ + "0.54, 0.587, 1.028"); } cell_fall(CELL_TABLE) { - values("0.556, 0.603, 1.044",\ - "0.556, 0.603, 1.044",\ - "0.556, 0.603, 1.044"); + values("0.54, 0.587, 1.028",\ + "0.54, 0.587, 1.028",\ + "0.54, 0.587, 1.028"); } rise_transition(CELL_TABLE) { values("0.024, 0.081, 0.61",\ @@ -298,19 +302,19 @@ cell (sram_2_16_1_scn3me_subm){ internal_power(){ when : "!CSb & clk & !WEb"; rise_power(scalar){ - values("10.9314668117"); + values("10.559086132533329"); } fall_power(scalar){ - values("10.9314668117"); + values("10.559086132533329"); } } internal_power(){ when : "!CSb & !clk & WEb"; rise_power(scalar){ - values("10.9314668117"); + values("10.559086132533329"); } fall_power(scalar){ - values("10.9314668117"); + values("10.559086132533329"); } } internal_power(){ @@ -336,10 +340,10 @@ cell (sram_2_16_1_scn3me_subm){ timing_type :"minimum_period"; related_pin : clk; rise_constraint(scalar) { - values("0.0"); + values("0"); } fall_constraint(scalar) { - values("0.0"); + values("0"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_pruned.lib b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_pruned.lib index b37a777f..39924746 100644 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_pruned.lib +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_pruned.lib @@ -78,27 +78,31 @@ cell (sram_2_16_1_scn3me_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 134589.78; + area : 142800.38999999998; leakage_power () { when : "CSb"; - value : 0.0004764706; + value : 0.0252988; } cell_leakage_power : 0; - bus(DATA){ + bus(DIN){ bus_type : DATA; - direction : inout; + direction : in; max_capacitance : 78.5936; min_capacitance : 2.45605; - three_state : "!OEb & !clk"; memory_write(){ address : ADDR; clocked_on : clk; } + bus(DOUT){ + bus_type : DATA; + direction : out; + max_capacitance : 78.5936; + min_capacitance : 2.45605; memory_read(){ address : ADDR; } - pin(DATA[1:0]){ + pin(DOUT[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk"; @@ -130,26 +134,26 @@ cell (sram_2_16_1_scn3me_subm){ timing(){ timing_sense : non_unate; related_pin : "clk"; - timing_type : falling_edge; + timing_type : rising_edge; cell_rise(CELL_TABLE) { - values("0.458, 0.504, 0.871",\ - "0.461, 0.506, 0.874",\ - "0.5, 0.544, 0.912"); + values("0.928, 0.959, 1.113",\ + "0.931, 0.962, 1.116",\ + "0.985, 1.018, 1.176"); } cell_fall(CELL_TABLE) { - values("0.573, 0.649, 1.251",\ - "0.577, 0.652, 1.254",\ - "0.618, 0.69, 1.29"); + values("11.214, 11.27, 11.756",\ + "11.22, 11.27, 11.761",\ + "11.268, 11.323, 11.809"); } rise_transition(CELL_TABLE) { - values("0.153, 0.233, 1.085",\ - "0.154, 0.234, 1.084",\ - "0.158, 0.237, 1.084"); + values("0.599, 0.624, 0.968",\ + "0.599, 0.623, 0.97",\ + "0.598, 0.623, 0.967"); } fall_transition(CELL_TABLE) { - values("0.276, 0.356, 1.5",\ - "0.277, 0.357, 1.5",\ - "0.278, 0.363, 1.5"); + values("11.157, 11.165, 11.446",\ + "11.159, 11.162, 1.271",\ + "11.158, 11.165, 11.47"); } } } @@ -298,19 +302,19 @@ cell (sram_2_16_1_scn3me_subm){ internal_power(){ when : "!CSb & clk & !WEb"; rise_power(scalar){ - values("4.42361814306"); + values("2.033783461111111"); } fall_power(scalar){ - values("4.42361814306"); + values("2.033783461111111"); } } internal_power(){ when : "!CSb & !clk & WEb"; rise_power(scalar){ - values("4.97118480973"); + values("2.032705683333334"); } fall_power(scalar){ - values("4.97118480973"); + values("2.032705683333334"); } } internal_power(){ @@ -326,20 +330,20 @@ cell (sram_2_16_1_scn3me_subm){ timing_type :"min_pulse_width"; related_pin : clk; rise_constraint(scalar) { - values("1.875"); + values("9.6875"); } fall_constraint(scalar) { - values("1.875"); + values("9.6875"); } } timing(){ timing_type :"minimum_period"; related_pin : clk; rise_constraint(scalar) { - values("3.75"); + values("19.375"); } fall_constraint(scalar) { - values("3.75"); + values("19.375"); } } } diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 6c7feddd..64c1c2b4 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -59,33 +59,78 @@ class openram_test(unittest.TestCase): # Reset the static duplicate name checker for unit tests. import hierarchy_design hierarchy_design.hierarchy_design.name_map=[] - + + def check_golden_data(self, data, golden_data, error_tolerance=1e-2): + """ + This function goes through two dictionaries, key by key and compares + each item. It uses relative comparisons for the items and returns false + if there is a mismatch. + """ + + # Check each result + data_matches = True + for k in data.keys(): + if type(data[k])==list: + for i in range(len(data[k])): + if not self.isclose(k,data[k][i],golden_data[k][i],error_tolerance): + data_matches = False + else: + self.isclose(k,data[k],golden_data[k],error_tolerance) + if not data_matches: + import pprint + data_string=pprint.pformat(data) + debug.error("Results exceeded {:.1f}% tolerance compared to golden results:\n".format(error_tolerance*100)+data_string) + return data_matches + - def isclose(self, value1,value2,error_tolerance=1e-2): + def isclose(self,key,value,actual_value,error_tolerance=1e-2): """ This is used to compare relative values. """ import debug - relative_diff = abs(value1 - value2) / max(value1,value2) + relative_diff = self.relative_diff(value,actual_value) check = relative_diff <= error_tolerance - if not check: - self.fail("NOT CLOSE {0} {1} relative diff={2}".format(value1,value2,relative_diff)) - else: - debug.info(2,"CLOSE {0} {1} relative diff={2}".format(value1,value2,relative_diff)) - - def relative_compare(self, value1,value2,error_tolerance): - """ This is used to compare relative values. """ - if (value1==value2): # if we don't need a relative comparison! + if check: + debug.info(2,"CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100)) return True - return (abs(value1 - value2) / max(value1,value2) <= error_tolerance) + else: + debug.error("NOT CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100)) + return False - def isapproxdiff(self, f1, f2, error_tolerance=0.001): + def relative_diff(self, value1, value2): + """ Compute the relative difference of two values and normalize to the largest. + If largest value is 0, just return the difference.""" + + # Edge case to avoid divide by zero + if value1==0 and value2==0: + return 0.0 + + # Don't need relative, exact compare + if value1==value2: + return 0.0 + + # Get normalization value + norm_value = abs(max(value1, value2)) + # Edge case where greater is a zero + if norm_value == 0: + min_value = abs(min(value1, value2)) + + return abs(value1 - value2) / norm_value + + + def relative_compare(self, value,actual_value,error_tolerance): + """ This is used to compare relative values. """ + if (value==actual_value): # if we don't need a relative comparison! + return True + return (abs(value - actual_value) / max(value,actual_value) <= error_tolerance) + + def isapproxdiff(self, filename1, filename2, error_tolerance=0.001): """Compare two files. Arguments: - f1 -- First file name + filename1 -- First file name - f2 -- Second file name + filename2 -- Second file name Return value: @@ -106,61 +151,93 @@ class openram_test(unittest.TestCase): (?: [Ee] [+-]? \d+ ) ? """ rx = re.compile(numeric_const_pattern, re.VERBOSE) - with open(f1, 'rb') as fp1, open(f2, 'rb') as fp2: - while True: - b1 = fp1.readline().decode('utf-8') - b2 = fp2.readline().decode('utf-8') - #print "b1:",b1, - #print "b2:",b2, + fp1 = open(filename1, 'rb') + fp2 = open(filename2, 'rb') + mismatches=0 + line_num=0 + while True: + line_num+=1 + line1 = fp1.readline().decode('utf-8') + line2 = fp2.readline().decode('utf-8') + #print("line1:",line1) + #print("line2:",line2) + + # 1. Find all of the floats using a regex + line1_floats=rx.findall(line1) + line2_floats=rx.findall(line2) + debug.info(3,"line1_floats: "+str(line1_floats)) + debug.info(3,"line2_floats: "+str(line2_floats)) - # 1. Find all of the floats using a regex - b1_floats=rx.findall(b1) - b2_floats=rx.findall(b2) - debug.info(3,"b1_floats: "+str(b1_floats)) - debug.info(3,"b2_floats: "+str(b2_floats)) - - # 2. Remove the floats from the string - for f in b1_floats: - b1=b1.replace(f,"",1) - for f in b2_floats: - b2=b2.replace(f,"",1) - #print "b1:",b1, - #print "b2:",b2, - # 3. Check if remaining string matches - if b1 != b2: - self.fail("MISMATCH Line: {0}\n!=\nLine: {1}".format(b1,b2)) + # 2. Remove the floats from the string + for f in line1_floats: + line1=line1.replace(f,"",1) + for f in line2_floats: + line2=line2.replace(f,"",1) + #print("line1:",line1) + #print("line2:",line2) - # 4. Now compare that the floats match - if len(b1_floats)!=len(b2_floats): - self.fail("MISMATCH Length {0} != {1}".format(len(b1_floats),len(b2_floats))) - for (f1,f2) in zip(b1_floats,b2_floats): - if not self.relative_compare(float(f1),float(f2),error_tolerance): - self.fail("MISMATCH Float {0} != {1}".format(f1,f2)) + # 3. Convert to floats rather than strings + line1_floats = [float(x) for x in line1_floats] + line2_floats = [float(x) for x in line1_floats] + + # 4. Check if remaining string matches + if line1 != line2: + if mismatches==0: + debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2)) + mismatches += 1 + debug.error("MISMATCH Line ({0}):\n{1}\n!=\n{2}".format(line_num,line1.rstrip('\n'),line2.rstrip('\n'))) - if not b1 and not b2: - return + # 5. Now compare that the floats match + elif len(line1_floats)!=len(line2_floats): + if mismatches==0: + debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2)) + mismatches += 1 + debug.error("MISMATCH Line ({0}) Length {1} != {2}".format(line_num,len(line1_floats),len(line2_floats))) + else: + for (float1,float2) in zip(line1_floats,line2_floats): + relative_diff = self.relative_diff(float1,float2) + check = relative_diff <= error_tolerance + if not check: + if mismatches==0: + debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2)) + mismatches += 1 + debug.error("MISMATCH Line ({0}) Float {1} != {2} diff: {3:.1f}%".format(line_num,float1,float2,relative_diff*100)) + + # Only show the first 10 mismatch lines + if not line1 and not line2 or mismatches>10: + fp1.close() + fp2.close() + return mismatches==0 + + # Never reached + return False - - def isdiff(self,file1,file2): + def isdiff(self,filename1,filename2): """ This is used to compare two files and display the diff if they are different.. """ import debug import filecmp import difflib - check = filecmp.cmp(file1,file2) + check = filecmp.cmp(filename1,filename2) if not check: - debug.info(2,"MISMATCH {0} {1}".format(file1,file2)) - f1 = open(file1,"r") - s1 = f1.readlines() - f2 = open(file2,"r") + debug.error("MISMATCH file1={0} file2={1}".format(filename1,filename2)) + f1 = open(filename1,"r") + s1 = f1.readlines().decode('utf-8') + f1.close() + f2 = open(filename2,"r").decode('utf-8') s2 = f2.readlines() + f2.close() + mismatches=0 for line in difflib.unified_diff(s1, s2): - debug.info(3,line) - self.fail("MISMATCH {0} {1}".format(file1,file2)) + mismatches += 1 + self.error("DIFF LINES:",line) + if mismatches>10: + return False + return False else: - debug.info(2,"MATCH {0} {1}".format(file1,file2)) - + debug.info(2,"MATCH {0} {1}".format(filename1,filename2)) + return True def header(filename, technology): # Skip the header for gitlab regression diff --git a/technology/freepdk45/sp_lib/cell_6t.sp b/technology/freepdk45/sp_lib/cell_6t.sp index 6b1be024..cb9cbc3c 100644 --- a/technology/freepdk45/sp_lib/cell_6t.sp +++ b/technology/freepdk45/sp_lib/cell_6t.sp @@ -1,10 +1,10 @@ .SUBCKT cell_6t bl br wl vdd gnd -MM3 bl wl net10 gnd NMOS_VTG W=135.00n L=50n -MM2 br wl net4 gnd NMOS_VTG W=135.00n L=50n -MM1 net10 net4 gnd gnd NMOS_VTG W=205.00n L=50n -MM0 net4 net10 gnd gnd NMOS_VTG W=205.00n L=50n -MM5 net10 net4 vdd vdd PMOS_VTG W=90n L=50n -MM4 net4 net10 vdd vdd PMOS_VTG W=90n L=50n +MM3 bl wl Q gnd NMOS_VTG W=135.00n L=50n +MM2 br wl Qb gnd NMOS_VTG W=135.00n L=50n +MM1 Q Qb gnd gnd NMOS_VTG W=205.00n L=50n +MM0 Qb Q gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q Qb vdd vdd PMOS_VTG W=90n L=50n +MM4 Qb Q vdd vdd PMOS_VTG W=90n L=50n .ENDS cell_6t diff --git a/technology/scn3me_subm/sp_lib/cell_6t.sp b/technology/scn3me_subm/sp_lib/cell_6t.sp index 0310b9fe..76c40f31 100644 --- a/technology/scn3me_subm/sp_lib/cell_6t.sp +++ b/technology/scn3me_subm/sp_lib/cell_6t.sp @@ -1,10 +1,10 @@ *********************** "cell_6t" ****************************** .SUBCKT cell_6t bl br wl vdd gnd -M_1 net_1 net_2 vdd vdd p W='0.9u' L=1.2u -M_2 net_2 net_1 vdd vdd p W='0.9u' L=1.2u -M_3 br wl net_2 gnd n W='1.2u' L=0.6u -M_4 bl wl net_1 gnd n W='1.2u' L=0.6u -M_5 net_2 net_1 gnd gnd n W='2.4u' L=0.6u -M_6 net_1 net_2 gnd gnd n W='2.4u' L=0.6u +M_1 Q Qb vdd vdd p W='0.9u' L=1.2u +M_2 Qb Q vdd vdd p W='0.9u' L=1.2u +M_3 br wl Qb gnd n W='1.2u' L=0.6u +M_4 bl wl Q gnd n W='1.2u' L=0.6u +M_5 Qb Q gnd gnd n W='2.4u' L=0.6u +M_6 Q Qb gnd gnd n W='2.4u' L=0.6u .ENDS $ cell_6t diff --git a/technology/scn3me_subm/sp_lib/write_driver.sp b/technology/scn3me_subm/sp_lib/write_driver.sp index a203d1ba..edddf18c 100644 --- a/technology/scn3me_subm/sp_lib/write_driver.sp +++ b/technology/scn3me_subm/sp_lib/write_driver.sp @@ -2,28 +2,28 @@ .SUBCKT write_driver din bl br en vdd gnd **** Inverter to conver Data_in to data_in_bar ****** -M_1 net_3 din gnd gnd n W='1.2*1u' L=0.6u -M_2 net_3 din vdd vdd p W='2.1*1u' L=0.6u +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 **** 2input nand gate follwed by inverter to drive BL ****** -M_3 net_2 en net_7 gnd n W='2.1*1u' L=0.6u +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 net_2 en vdd vdd p W='2.1*1u' L=0.6u -M_6 net_2 din vdd vdd p 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 net_2 vdd vdd p W='2.1*1u' L=0.6u -M_8 net_1 net_2 gnd gnd n W='1.2*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 **** 2input nand gate follwed by inverter to drive BR****** -M_9 net_4 en vdd vdd p W='2.1*1u' L=0.6u -M_10 net_4 en net_8 gnd n W='2.1*1u' L=0.6u -M_11 net_8 net_3 gnd gnd n W='2.1*1u' L=0.6u -M_12 net_4 net_3 vdd vdd p W='2.1*1u' L=0.6u +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 net_4 vdd vdd p W='2.1*1u' L=0.6u -M_14 net_6 net_4 gnd gnd n W='1.2*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 ************************************************