From 20d8c0bc45416a2391d46aeb4a481d2241c28de4 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 6 Jul 2017 08:42:25 -0700 Subject: [PATCH] Improved characterizer. --- compiler/bank.py | 26 +- compiler/bitcell.py | 10 +- compiler/bitcell_array.py | 16 +- compiler/characterizer/charutils.py | 31 +- compiler/characterizer/delay.py | 484 +++++++++--------- compiler/characterizer/lib.py | 289 +++++++---- compiler/characterizer/setup_hold.py | 442 ++++++++-------- compiler/characterizer/stimuli.py | 377 ++++---------- compiler/debug.py | 2 +- compiler/globals.py | 19 +- compiler/hierarchical_decoder.py | 8 +- compiler/hierarchical_predecode2x4.py | 8 +- compiler/hierarchical_predecode3x8.py | 6 +- compiler/hierarchy_spice.py | 40 +- compiler/ms_flop.py | 8 +- compiler/ms_flop_array.py | 4 +- compiler/nand_2.py | 8 +- compiler/nand_3.py | 8 +- compiler/nor_2.py | 2 +- compiler/pinv.py | 8 +- compiler/sense_amp.py | 8 +- compiler/sense_amp_array.py | 4 +- compiler/sram.py | 33 +- compiler/tests/21_hspice_delay_test.py | 37 +- compiler/tests/21_hspice_hold_test.py | 53 -- ...up_test.py => 21_hspice_setuphold_test.py} | 23 +- compiler/tests/21_ngspice_delay_test.py | 45 +- compiler/tests/21_ngspice_setup_test.py | 55 -- ...d_test.py => 21_ngspice_setuphold_test.py} | 22 +- compiler/tests/22_sram_func_test.py | 5 +- .../tests/golden/sram_2_16_1_freepdk45.lib | 240 +++++---- .../sram_2_16_1_freepdk45_analytical.lib | 232 +++++---- .../tests/golden/sram_2_16_1_scn3me_subm.lib | 240 +++++---- .../sram_2_16_1_scn3me_subm_analytical.lib | 232 +++++---- compiler/tests/testutils.py | 10 +- compiler/tri_gate.py | 6 +- compiler/tri_gate_array.py | 26 +- compiler/wordline_driver.py | 6 +- technology/freepdk45/tech/tech.py | 31 +- technology/scn3me_subm/tech/tech.py | 33 +- 40 files changed, 1511 insertions(+), 1626 deletions(-) delete mode 100644 compiler/tests/21_hspice_hold_test.py rename compiler/tests/{21_hspice_setup_test.py => 21_hspice_setuphold_test.py} (58%) delete mode 100644 compiler/tests/21_ngspice_setup_test.py rename compiler/tests/{21_ngspice_hold_test.py => 21_ngspice_setuphold_test.py} (60%) diff --git a/compiler/bank.py b/compiler/bank.py index d2193292..1114a267 100644 --- a/compiler/bank.py +++ b/compiler/bank.py @@ -214,11 +214,6 @@ class bank(design.design): word_size=self.word_size) self.add_mod(self.msf_data_in) - self.msf_data_out = self.mod_ms_flop_array(name="msf_data_out", - columns=self.num_cols, - word_size=self.word_size) - self.add_mod(self.msf_data_out) - self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols, word_size=self.word_size) self.add_mod(self.tri_gate_array) @@ -747,7 +742,7 @@ class bank(design.design): """ Routing of sense amp output to tri_gate input """ for i in range(self.word_size): # Connection of data_out of sense amp to data_ in of msf_data_out - tri_gate_in_position = (self.tri_gate_array.tri_in_positions[i].scale(1,-1) + tri_gate_in_position = (self.tri_gate_array.in_positions[i].scale(1,-1) + self.tri_gate_array_offset) sa_data_out_position = (self.sens_amp_array_position + self.sens_amp_array.Data_out_positions[i]) @@ -773,7 +768,7 @@ class bank(design.design): def route_tri_gate_out(self): """ Metal 3 routing of tri_gate output data """ for i in range(self.word_size): - tri_gate_out_position = (self.tri_gate_array.DATA_positions[i].scale(1,-1) + tri_gate_out_position = (self.tri_gate_array.out_positions[i].scale(1,-1) + self.tri_gate_array_offset) data_line_position = [tri_gate_out_position.x - 0.5 * drc["minwidth_metal3"], self.min_point] @@ -1511,24 +1506,21 @@ class bank(design.design): y_offset], mirror="R90") - def delay(self, slope): + def delay(self, slew, load): """ return analytical delay of the bank""" - msf_addr_delay = self.msf_address.delay(slope, - self.decoder.input_load()) + msf_addr_delay = self.msf_address.delay(slew, self.decoder.input_load()) - decoder_delay = self.decoder.delay(msf_addr_delay.slope, - self.wordline_driver.input_load()) + decoder_delay = self.decoder.delay(msf_addr_delay.slew, self.wordline_driver.input_load()) - word_driver_delay = self.wordline_driver.delay(decoder_delay.slope, - self.bitcell_array.input_load()) + word_driver_delay = self.wordline_driver.delay(decoder_delay.slew, self.bitcell_array.input_load()) - bitcell_array_delay = self.bitcell_array.delay(word_driver_delay.slope) + bitcell_array_delay = self.bitcell_array.delay(word_driver_delay.slew) - bl_t_data_out_delay = self.sens_amp_array.delay(bitcell_array_delay.slope, + bl_t_data_out_delay = self.sens_amp_array.delay(bitcell_array_delay.slew, self.bitcell_array.output_load()) # output load of bitcell_array is set to be only small part of bl for sense amp. - data_t_DATA_delay = self.tri_gate_array.delay(bl_t_data_out_delay.slope) + data_t_DATA_delay = self.tri_gate_array.delay(bl_t_data_out_delay.slew, load) result = msf_addr_delay + decoder_delay + word_driver_delay \ + bitcell_array_delay + bl_t_data_out_delay + data_t_DATA_delay diff --git a/compiler/bitcell.py b/compiler/bitcell.py index 81127a4f..8929324c 100644 --- a/compiler/bitcell.py +++ b/compiler/bitcell.py @@ -21,14 +21,14 @@ class bitcell(design.design): self.width = bitcell.chars["width"] self.height = bitcell.chars["height"] - def delay(self, slope, load=0, swing = 0.5): + def delay(self, slew, load=0, swing = 0.5): # delay of bit cell is not like a driver(from WL) - # so the slope used should be 0 - # it should not be slope dependent? + # so the slew used should be 0 + # it should not be slew dependent? # because the value is there # the delay is only over half transsmission gate from tech import spice r = spice["min_tx_r"]*3 - c_para = spice["min_tx_c_para"]#ff - result = self.cal_delay_with_rc(r = r, c = c_para+load, slope = slope, swing = swing) + c_para = spice["min_tx_drain_c"] + result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew, swing = swing) return result diff --git a/compiler/bitcell_array.py b/compiler/bitcell_array.py index 9dec605f..b14fd6ae 100644 --- a/compiler/bitcell_array.py +++ b/compiler/bitcell_array.py @@ -143,26 +143,22 @@ class bitcell_array(design.design): # increments to the next column width offset.x += self.cell.width - def delay(self, slope, load=0): + def delay(self, slew, load=0): from tech import drc wl_wire = self.gen_wl_wire() - wl_wire.return_delay_over_wire(slope) + wl_wire.return_delay_over_wire(slew) - wl_to_cell_delay = wl_wire.return_delay_over_wire(slope) + wl_to_cell_delay = wl_wire.return_delay_over_wire(slew) # hypothetical delay from cell to bl end without sense amp bl_wire = self.gen_bl_wire() cell_load = 2 * bl_wire.return_input_cap() # we ingore the wire r # hence just use the whole c bl_swing = 0.1 - cell_delay = self.cell.delay(wl_to_cell_delay.slope, cell_load, swing = bl_swing) + cell_delay = self.cell.delay(wl_to_cell_delay.slew, cell_load, swing = bl_swing) #we do not consider the delay over the wire for now - #bl_wire_delay = bl_wire.return_delay_over_wire(cell_delay.slope, swing = bl_swing) - #return [wl_to_cell_delay, cell_delay, bl_wire_delay] - #return self.return_delay(cell_delay.delay+wl_to_cell_delay.delay+bl_wire_delay.delay, - # bl_wire_delay.slope) return self.return_delay(cell_delay.delay+wl_to_cell_delay.delay, - wl_to_cell_delay.slope) + wl_to_cell_delay.slew) def gen_wl_wire(self): wl_wire = self.generate_rc_net(int(self.column_size), self.width, drc["minwidth_metal1"]) @@ -172,7 +168,7 @@ class bitcell_array(design.design): def gen_bl_wire(self): bl_pos = 0 bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), self.height, drc["minwidth_metal1"]) - bl_wire.wire_c =spice["min_tx_c_para"] + bl_wire.wire_c # 1 access tx d/s per cell + bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell return bl_wire def output_load(self, bl_pos=0): diff --git a/compiler/characterizer/charutils.py b/compiler/characterizer/charutils.py index 009e1d82..4f958a13 100644 --- a/compiler/characterizer/charutils.py +++ b/compiler/characterizer/charutils.py @@ -4,23 +4,8 @@ import debug OPTS = globals.get_opts() -# 0.1% is the relative tolerance for convergence -error_tolerance = 0.001 - -# times are in ns, so this is how many digits of precision -# 3 digits = 1ps -# 4 digits = 0.1ps -# etc. -time_precision = 3 -# voltages are in volts -# 3 digits = 1mv -# 4 digits = 0.1mv -# 5 digits = 0.01mv -# 6 digits = 1uv -# etc -voltage_precision = 5 -def relative_compare(value1,value2): +def relative_compare(value1,value2,error_tolerance=0.001): """ This is used to compare relative values for convergence. """ return (abs(value1 - value2) / max(value1,value2) <= error_tolerance) @@ -40,10 +25,20 @@ def parse_output(filename, key): else: return "Failed" -def round_time(time): +def round_time(time,time_precision=3): + # times are in ns, so this is how many digits of precision + # 3 digits = 1ps + # 4 digits = 0.1ps + # etc. return round(time,time_precision) -def round_voltage(voltage): +def round_voltage(voltage,voltag_precision=5): + # voltages are in volts + # 3 digits = 1mv + # 4 digits = 0.1mv + # 5 digits = 0.01mv + # 6 digits = 1uv + # etc return round(voltage,voltage_precision) def convert_to_float(number): diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 11ff5b60..89ad72a9 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -22,7 +22,9 @@ class delay(): self.word_size = sram.word_size self.addr_size = sram.addr_size self.sram_sp_file = spfile - + + self.vdd = tech.spice["supply_voltage"] + self.gnd = tech.spice["gnd_voltage"] def check_arguments(self): """Checks if arguments given for write_stimulus() meets requirements""" @@ -38,7 +40,7 @@ class delay(): debug.error("Given probe_data is not an integer to specify a data bit",1) - def write_stimulus(self, feasible_period, target_period, data_value): + def write_stimulus(self, period, load, slew): """Creates a stimulus file for simulations to probe a certain bitcell, given an address and data-position of the data-word (probe-address form: '111010000' LSB=0, MSB=1) (probe_data form: number corresponding to the bit position of data-bus, begins with position 0) @@ -46,28 +48,21 @@ class delay(): self.check_arguments() # obtains list of time-points for each rising clk edge - self.obtain_cycle_times(slow_period=feasible_period, - fast_period=target_period) + self.obtain_cycle_times(period) # 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("* Stimulus data value of {0} for target period of {1}n\n".format(data_value, target_period)) - self.sf.write("\n") + self.sf.write("* Stimulus for period of {0}n load={1} slew={2}\n\n".format(period,load,slew)) # include files in stimulus file model_list = tech.spice["fet_models"] + [self.sram_sp_file] stimuli.write_include(stim_file=self.sf, models=model_list) - self.sf.write("\n") # add vdd/gnd statements + self.sf.write("* Global Power Supplies\n") - stimuli.write_supply(stim_file=self.sf, - vdd_name=tech.spice["vdd_name"], - gnd_name=tech.spice["gnd_name"], - vdd_voltage=tech.spice["supply_voltage"], - gnd_voltage=tech.spice["gnd_voltage"]) - self.sf.write("\n") + stimuli.write_supply(self.sf) # instantiate the sram self.sf.write("* Instantiation of the SRAM\n") @@ -75,210 +70,144 @@ class delay(): abits=self.addr_size, dbits=self.word_size, sram_name=self.name) - self.sf.write("\n") - # create a buffer and an inverter - self.sf.write("* Buffers and inverter Initialization\n") - # FIXME: We should replace the clock buffer with the same - # 2x buffer for control signals. This needs the buffer to be - # added to the control logic though. - stimuli.create_buffer(stim_file=self.sf, - buffer_name="clk1_buffer", - size=[1, 4]) - self.sf.write("\n") - stimuli.create_buffer(stim_file=self.sf, - buffer_name="clk2_buffer", - size=[8, 16]) - self.sf.write("\n") - - stimuli.create_buffer(stim_file=self.sf, - buffer_name="buffer", - size=[1, 2]) - self.sf.write("\n") - - stimuli.create_inverter(stim_file=self.sf) - self.sf.write("\n") - - # add a buffer for each signal and an inverter for WEb - signal_list = [] + self.sf.write("* SRAM output loads\n") for i in range(self.word_size): - signal_list.append("D[{0}]".format(i)) - for j in range(self.addr_size): - signal_list.append("A[{0}]".format(j)) - for k in tech.spice["control_signals"]: - signal_list.append(k) - self.sf.write("*Buffers for each generated signal and Inv for WEb\n") - stimuli.add_buffer(stim_file=self.sf, - buffer_name="buffer", - signal_list=signal_list) - stimuli.add_buffer(stim_file=self.sf, - buffer_name="clk1_buffer", - signal_list=["clk"]) - stimuli.add_buffer(stim_file=self.sf, - buffer_name="clk2_buffer", - signal_list=["clk_buf"]) - stimuli.add_buffer(stim_file=self.sf, - buffer_name="buffer", - signal_list=["WEb_trans"]) - stimuli.add_inverter(stim_file=self.sf, - signal_list=["WEb_trans"]) - self.sf.write("\n") - + self.sf.write("CD{0} D[{0}] 0 {1}f\n".format(i,load)) + # add access transistors for data-bus - self.sf.write("* Transmission Gates for data-bus\n") - stimuli.add_accesstx(stim_file=self.sf, dbits=self.word_size) - self.sf.write("\n") + self.sf.write("* Transmission Gates for data-bus and control signals\n") + stimuli.inst_accesstx(stim_file=self.sf, dbits=self.word_size) # generate data and addr signals - self.sf.write("*Generation of data and address signals\n") - if data_value == tech.spice["supply_voltage"]: - v_val = tech.spice["gnd_voltage"] - else: - v_val = tech.spice["supply_voltage"] + self.sf.write("* Generation of data and address signals\n") for i in range(self.word_size): if i == self.probe_data: - stimuli.gen_data_pwl(stim_file=self.sf, - key_times=self.cycle_times, - sig_name="D[{0}]".format(i), - data_value=data_value, - feasible_period=feasible_period, - target_period=target_period, - t_rise=tech.spice["rise_time"], - t_fall=tech.spice["fall_time"]) + stimuli.gen_data(stim_file=self.sf, + clk_times=self.cycle_times, + sig_name="DATA[{0}]".format(i), + period=period, + slew=slew) else: stimuli.gen_constant(stim_file=self.sf, sig_name="D[{0}]".format(i), - v_ref=tech.spice["gnd_voltage"], - v_val=v_val) + v_val=self.gnd) - stimuli.gen_addr_pwl(stim_file=self.sf, - key_times=self.cycle_times, - addr=self.probe_address, - feasible_period=feasible_period, - target_period=target_period, - t_rise=tech.spice["rise_time"], - t_fall=tech.spice["fall_time"]) - self.sf.write("\n") + stimuli.gen_addr(self.sf, + clk_times=self.cycle_times, + addr=self.probe_address, + period=period, + slew=slew) # generate control signals - self.sf.write("*Generation of control signals\n") - # CSb - (x_list, y_list) = stimuli.gen_csb_pwl(key_times=self.cycle_times, - feasible_period=feasible_period, - target_period=target_period, - t_rise=tech.spice["rise_time"], - t_fall=tech.spice["fall_time"]) - stimuli.gen_pwl(stim_file=self.sf, - sig_name="CSb", - x_list=x_list, - y_list=y_list) - # WEb - (x_list, y_list) = stimuli.gen_web_pwl(key_times=self.cycle_times, - feasible_period=feasible_period, - target_period=target_period, - t_rise=tech.spice["rise_time"], - t_fall=tech.spice["fall_time"]) - stimuli.gen_pwl(stim_file=self.sf, - sig_name="WEb", - x_list=x_list, - y_list=y_list) - # OEb - (x_list, y_list) = stimuli.gen_oeb_pwl(key_times=self.cycle_times, - feasible_period=feasible_period, - target_period=target_period, - t_rise=tech.spice["rise_time"], - t_fall=tech.spice["fall_time"]) - stimuli.gen_pwl(stim_file=self.sf, - sig_name="OEb", - x_list=x_list, - y_list=y_list) - # WEb_transmission_gate - (x_list, y_list) = stimuli.gen_web_trans_pwl(key_times=self.cycle_times, - feasible_period=feasible_period, - target_period=target_period, - t_rise=tech.spice["rise_time"], - t_fall=tech.spice["fall_time"]) - stimuli.gen_pwl(stim_file=self.sf, - sig_name="WEb_trans", - x_list=x_list, - y_list=y_list) - self.sf.write("\n") + self.sf.write("* Generation of control signals\n") + stimuli.gen_csb(self.sf, self.cycle_times, period, slew) + stimuli.gen_web(self.sf, self.cycle_times, period, slew) + stimuli.gen_oeb(self.sf, self.cycle_times, period, slew) - self.write_clock() + self.sf.write("* Generation of global clock signal\n") + stimuli.gen_pulse(stim_file=self.sf, + sig_name="CLK", + v1=self.gnd, + v2=self.vdd, + offset=period, + period=period, + t_rise = slew, + t_fall = slew) + + self.write_measures(period) - self.write_measures(data_value) - - self.write_control() + # run until the last cycle time + stimuli.write_control(self.sf,self.cycle_times[-1]) self.sf.close() - def write_clock(self): - # generate clk PWL based on the clock periods - self.sf.write("* Generation of global clock signal\n") - stimuli.gen_clk_pwl(stim_file=self.sf, - cycle_times=self.cycle_times, - t_rise=tech.spice["rise_time"], - t_fall=tech.spice["fall_time"]) - self.sf.write("\n") - - def write_measures(self, data_value): + def write_measures(self,period): # meas statement for delay and power measurements self.sf.write("* Measure statements for delay and power\n") - # add measure statments for delay - trig_name = tech.spice["clk"] + "_buf_buf" - targ_name = "{0}".format("DATA[{0}]".format(self.probe_data)) - trig_val = targ_val = 0.5 * tech.spice["supply_voltage"] - trig_dir = self.read_cycle - targ_dir = "RISE" if data_value == tech.spice["supply_voltage"] else "FALL" - td = self.cycle_times[self.clear_bus_cycle] + trig_name = "clk" + targ_name = "{0}".format("D[{0}]".format(self.probe_data)) + trig_val = targ_val = 0.5 * self.vdd + # add measure statments for delay0 + # delay the target to measure after the negetive edge stimuli.gen_meas_delay(stim_file=self.sf, - meas_name="DELAY", + meas_name="DELAY0", trig_name=trig_name, targ_name=targ_name, trig_val=trig_val, targ_val=targ_val, - trig_dir=trig_dir, - targ_dir=targ_dir, - td=td) + trig_dir="FALL", + targ_dir="FALL", + td=self.cycle_times[self.read0_cycle]+0.5*period) + stimuli.gen_meas_delay(stim_file=self.sf, + meas_name="DELAY1", + trig_name=trig_name, + targ_name=targ_name, + trig_val=trig_val, + targ_val=targ_val, + trig_dir="FALL", + targ_dir="RISE", + td=self.cycle_times[self.read1_cycle]+0.5*period) + + stimuli.gen_meas_delay(stim_file=self.sf, + meas_name="SLEW0", + trig_name=targ_name, + targ_name=targ_name, + trig_val=0.9*self.vdd, + targ_val=0.1*self.vdd, + trig_dir="FALL", + targ_dir="FALL", + td=self.cycle_times[self.read0_cycle]+0.5*period) + + stimuli.gen_meas_delay(stim_file=self.sf, + meas_name="SLEW1", + trig_name=targ_name, + targ_name=targ_name, + trig_val=0.1*self.vdd, + targ_val=0.9*self.vdd, + trig_dir="RISE", + targ_dir="RISE", + td=self.cycle_times[self.read1_cycle]+0.5*period) + # add measure statements for power - t_initial = self.cycle_times[self.write_cycle] - t_final = self.cycle_times[self.write_cycle+1] + t_initial = self.cycle_times[self.write0_cycle] + t_final = self.cycle_times[self.write0_cycle+1] stimuli.gen_meas_power(stim_file=self.sf, - meas_name="POWER_WRITE", + meas_name="WRITE0_POWER", t_initial=t_initial, t_final=t_final) - t_initial = self.cycle_times[self.read_cycle] - t_final = self.cycle_times[self.read_cycle+1] + t_initial = self.cycle_times[self.write1_cycle] + t_final = self.cycle_times[self.write1_cycle+1] stimuli.gen_meas_power(stim_file=self.sf, - meas_name="POWER_READ", + meas_name="WRITE1_POWER", + t_initial=t_initial, + t_final=t_final) + + t_initial = self.cycle_times[self.read0_cycle] + t_final = self.cycle_times[self.read0_cycle+1] + stimuli.gen_meas_power(stim_file=self.sf, + meas_name="READ0_POWER", t_initial=t_initial, t_final=t_final) - self.sf.write("\n") - - def write_control(self): - # run until the last cycle time - end_time = self.cycle_times[-1] - self.sf.write(".TRAN 5p {0}n\n".format(end_time)) - self.sf.write(".OPTIONS POST=1 PROBE\n") - # create plots for all signals - self.sf.write(".probe V(*)\n") - # end the stimulus file - self.sf.write(".end\n") - - - - def find_feasible_period(self,initial_period): + t_initial = self.cycle_times[self.read1_cycle] + t_final = self.cycle_times[self.read1_cycle+1] + stimuli.gen_meas_power(stim_file=self.sf, + meas_name="READ1_POWER", + t_initial=t_initial, + t_final=t_final) + + def find_feasible_period(self, load, slew): """Uses an initial period and finds a feasible period before we run the binary search algorithm to find min period. We check if the given clock period is valid and if it's not, we continue to double the period until we find a valid period to use as a starting point. """ - feasible_period = initial_period + feasible_period = tech.spice["feasible_period"] time_out = 8 while True: debug.info(1, "Finding feasible period: {0}ns".format(feasible_period)) @@ -287,52 +216,47 @@ class delay(): if (time_out <= 0): debug.error("Timed out, could not find a feasible period.",2) - (success, delay_out)=self.try_period(feasible_period,feasible_period,tech.spice["supply_voltage"]) + (success, feasible_delay1, feasible_slew1, feasible_delay0, feasible_slew0)=self.run_simulation(feasible_period,load,slew) if not success: feasible_period = 2 * feasible_period continue - (success, delay_out)=self.try_period(feasible_period,feasible_period,tech.spice["gnd_voltage"]) - if not success: - feasible_period = 2 * feasible_period - continue - - debug.info(1, "Starting Binary Search Algorithm with feasible_period: {0}ns".format(feasible_period)) - return feasible_period + debug.info(1, "Found feasible_period: {0}ns feasible_delay1/0 {1}ns/{2}ns".format(feasible_period,feasible_delay1,feasible_delay0)) + return (feasible_period, feasible_delay1, feasible_delay0) - def try_period(self, feasible_period, target_period, data_value): + def run_simulation(self, period, load, slew): """ This tries to simulate a period and checks if the result - works. If so, it returns True. If not, it it doubles the - period and returns False.""" + works. If so, it returns True and the delays and slews.""" # Checking from not data_value to data_value - self.write_stimulus(feasible_period, target_period, data_value) + self.write_stimulus(period, load, slew) stimuli.run_sim() - delay_value = ch.convert_to_float(ch.parse_output("timing", "delay")) + delay0 = ch.convert_to_float(ch.parse_output("timing", "delay0")) + delay1 = ch.convert_to_float(ch.parse_output("timing", "delay1")) + slew0 = ch.convert_to_float(ch.parse_output("timing", "slew0")) + slew1 = ch.convert_to_float(ch.parse_output("timing", "slew1")) # if it failed or the read was longer than a period - if type(delay_value)!=float or delay_value*1e9>target_period: - debug.info(2,"Infeasible period " + str(target_period) + " delay " + str(delay_value*1e9) + "ns") - return (False, "NA") + if type(delay0)!=float or type(delay1)!=float or type(slew1)!=float or type(slew0)!=float: + return (False,0,0,0,0) else: - debug.info(2,"Feasible period " + str(feasible_period) \ - + ", target period " + str(target_period) \ - + ", read/write of " + str(data_value) \ - + ", delay=" + str(delay_value*1e9) + "ns") + delay0 *= 1e9 + delay1 *= 1e9 + slew0 *= 1e9 + slew1 *= 1e9 + debug.info(2,"Simulation w/ period {0}, delay0={1}n delay1={2}ns".format(period,delay0,delay1)) #key=raw_input("press return to continue") - return (True, delay_value*1e9) + # The delay is from the negative edge for our SRAM + return (True,delay1,slew1,delay0,slew0) - def find_min_period(self,data_value): - """Creates a spice test and instantiates a single SRAM for - testing. Returns a tuple of the lowest period and its - clk-to-q delay for the specified data_value.""" - # Find a valid and feasible period before starting the binary search - # We just try 2.0ns here, but any value would work albeit slower. - feasible_period = self.find_feasible_period(tech.spice["feasible_period"]) + def find_min_period(self,feasible_period, load, slew, feasible_delay1, feasible_delay0): + """Searches for the smallest period with output delays being within 5% of + long period. """ + previous_period = ub_period = feasible_period lb_period = 0.0 @@ -348,93 +272,145 @@ class delay(): ub_period, lb_period)) - (success, delay_out) = self.try_period(feasible_period, target_period, data_value) - if success: + if self.try_period(target_period, load, slew, feasible_delay1, feasible_delay0): ub_period = target_period else: lb_period = target_period - if ch.relative_compare(ub_period, lb_period): - # use the two values to compare, but only return the ub since it is guaranteed feasible - (success, delay_out) = self.try_period(feasible_period, ub_period, data_value) - return (ub_period, delay_out) + if ch.relative_compare(ub_period, lb_period, error_tolerance=0.05): + # ub_period is always feasible + return ub_period - self.error("Should not reach here.",-1) - return (target_period, delay_out) + def try_period(self, period, load, slew, feasible_delay1, feasible_delay0): + """ This tries to simulate a period and checks if the result + works. If it does and the delay is within 5% still, it returns True.""" + + # Checking from not data_value to data_value + self.write_stimulus(period,load,slew) + stimuli.run_sim() + delay0 = ch.convert_to_float(ch.parse_output("timing", "delay0")) + delay1 = ch.convert_to_float(ch.parse_output("timing", "delay1")) + if type(delay0)==float: + delay0 *= 1e9 + if type(delay1)==float: + delay1 *= 1e9 + debug.info(2,"Period {0}, delay0={1}ns, delay1={2}ns".format(period,delay0, delay1)) + # if it failed or the read was longer than a period + if type(delay0)!=float or type(delay1)!=float: + return False + else: + if ch.relative_compare(delay1*1e9,feasible_delay1,error_tolerance=0.05): + return False + elif ch.relative_compare(delay0*1e9,feasible_delay0,error_tolerance=0.05): + return False + + + #key=raw_input("press return to continue") + + return True + def set_probe(self,probe_address, probe_data): """ Probe address and data can be set separately to utilize other functions in this characterizer besides analyze.""" self.probe_address = probe_address self.probe_data = probe_data - def analyze(self,probe_address, probe_data): + def analyze(self,probe_address, probe_data, slews, loads): """main function to calculate the min period for a low_to_high transistion and a high_to_low transistion returns a dictionary that contains all both the min period and associated delays Dictionary Keys: min_period1, delay1, min_period0, delay0 """ + self.set_probe(probe_address, probe_data) - (min_period1, delay1) = self.find_min_period(tech.spice["supply_voltage"]) - if (min_period1 == None) or (delay1 == None): - return None - debug.info(1, "Min Period for low_to_high transistion: {0}n with a delay of {1}".format(min_period1, delay1)) - (min_period0, delay0) = self.find_min_period(tech.spice["gnd_voltage"]) - if (min_period0 == None) or (delay0 == None): - return None - debug.info(1, "Min Period for high_to_low transistion: {0}n with a delay of {1}".format(min_period0, delay0)) - read_power=ch.convert_to_float(ch.parse_output("timing", "power_read")) - write_power=ch.convert_to_float(ch.parse_output("timing", "power_write")) + (feasible_period, feasible_delay1, feasible_delay0) = self.find_feasible_period(loads[0], slews[0]) + debug.check(feasible_delay1>0,"Negative delay may not be possible") + debug.check(feasible_delay0>0,"Negative delay may not be possible") - data = {"min_period1": min_period1, # period in ns - "delay1": delay1, # delay in s - "min_period0": min_period0, - "delay0": delay0, - "read_power": read_power, - "write_power": write_power + # The power variables are just scalars. These use the final feasible period simulation + # which should have worked. + read0_power=ch.convert_to_float(ch.parse_output("timing", "read0_power")) + write0_power=ch.convert_to_float(ch.parse_output("timing", "write0_power")) + read1_power=ch.convert_to_float(ch.parse_output("timing", "read1_power")) + write1_power=ch.convert_to_float(ch.parse_output("timing", "write1_power")) + + LH_delay = [] + HL_delay = [] + LH_slew = [] + HL_slew = [] + for slew in slews: + for load in loads: + (success, delay1, slew1, delay0, slew0) = self.run_simulation(feasible_period, load, slew) + debug.check(success,"Couldn't run a simulation properly.\n") + LH_delay.append(delay1) + HL_delay.append(delay0) + LH_slew.append(slew1) + HL_slew.append(slew0) + + # finds the minimum period without degrading the delays by X% + min_period = self.find_min_period(feasible_period, loads[0], slews[0], feasible_delay1, feasible_delay0) + debug.check(type(min_period)==float,"Couldn't find minimum period.") + debug.info(1, "Min Period: {0}n with a delay of {1}".format(min_period, feasible_delay1)) + + + data = {"min_period": ch.round_time(min_period), + "delay1": LH_delay, + "delay0": HL_delay, + "slew1": LH_slew, + "slew0": HL_slew, + "read0_power": read0_power*1e3, + "read1_power": read1_power*1e3, + "write0_power": write0_power*1e3, + "write1_power": write1_power*1e3 } return data - def obtain_cycle_times(self, slow_period, fast_period): + def obtain_cycle_times(self, period): """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.""" - # idle: half cycle, no operation - t_current = 0.5 * slow_period - - # slow cycle1: W data 1 address 1111 to initialize cell to a value - self.cycle_times = [t_current] - self.init_cycle=1 - t_current += slow_period - - # slow cycle2: R data 1 address 1111 (to ensure cell was written to opposite data value) + # idle cycle, no operation + t_current = period + self.cycle_times = [] + + # cycle0: W data 1 address 1111 to initialize cell to a value self.cycle_times.append(t_current) - self.verify_init_cycle=2 - t_current += slow_period + t_current += period - # fast cycle3: W data 0 address 1111 (to ensure a write of opposite value works) + # cycle1: W data 0 address 1111 (to ensure a write of value works) self.cycle_times.append(t_current) - self.write_cycle=3 - t_current += fast_period - - # fast cycle4: W data 1 address 0000 (to invert the bus cap from prev write) + self.write0_cycle=1 + t_current += period + + # cycle2: W data 1 address 0000 (to clear the data bus cap) self.cycle_times.append(t_current) - self.clear_bus_cycle=4 - t_current += fast_period + t_current += period - # fast cycle5: R data 0 address 1111 (to ensure that a read works and gets the value) + # cycle3: R data 0 address 1111 to check W0 works self.cycle_times.append(t_current) - self.read_cycle=5 - t_current += fast_period + self.read0_cycle=3 + t_current += period - # slow cycle 6: wait a slow clock period to end the simulation + # cycle4: W data 1 address 1111 (to ensure a write of value works) self.cycle_times.append(t_current) - self.wait_cycle=6 - t_current += slow_period + self.write1_cycle=4 + t_current += period - # end time of simulation + # cycle5: W data 0 address 0000 (to clear the data bus cap) self.cycle_times.append(t_current) + t_current += period + + # cycle6: R data 1 address 1111 to check W1 works + self.cycle_times.append(t_current) + self.read1_cycle=6 + t_current += period + + # cycle7: wait a clock period to end the simulation + self.cycle_times.append(t_current) + t_current += period + diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 25d46ac0..124cb91a 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -8,7 +8,8 @@ import math import setup_hold import delay import charutils as ch - +import tech +import numpy as np OPTS = globals.get_opts() @@ -16,24 +17,53 @@ class lib: """ lib file generation.""" def __init__(self, libname, sram, spfile, use_model=OPTS.analytical_delay): + self.sram = sram + self.spfile = spfile + self.use_model = use_model self.name = sram.name self.num_words = sram.num_words self.word_size = sram.word_size self.addr_size = sram.addr_size - self.sh = setup_hold.setup_hold() - self.d = delay.delay(sram, spfile) - + # These are the parameters to determine the table sizes + #self.load_scales = np.array([0.1, 0.25, 0.5, 1, 2, 4, 8]) + self.load_scales = np.array([0.25, 1, 8]) + #self.load_scales = np.array([0.25, 1]) + self.load = tech.spice["FF_in_cap"] + self.loads = self.load_scales*self.load + debug.info(1,"Loads: {0}".format(self.loads)) + + #self.slew_scales = np.array([0.1, 0.25, 0.5, 1, 2, 4, 8]) + self.slew_scales = np.array([0.25, 1, 8]) + #self.slew_scales = np.array([0.25, 1]) + self.slew = tech.spice["rise_time"] + self.slews = self.slew_scales*self.slew + debug.info(1,"Slews: {0}".format(self.slews)) + debug.info(1,"Writing to {0}".format(libname)) self.lib = open(libname, "w") + self.write_header() + + self.write_data_bus() + + self.write_addr_bus() + + self.write_control_pins() + + self.write_clk() + + self.lib.close() + + def write_header(self): + """ Write the header information """ self.lib.write("library ({0}_lib)".format(self.name)) self.lib.write("{\n") self.lib.write(" delay_model : \"table_lookup\";\n") self.write_units() self.write_defaults() - self.write_LUT() + self.write_LUT_templates() self.lib.write(" default_operating_conditions : TT; \n") @@ -50,34 +80,7 @@ class lib: self.lib.write(" dont_use : true;\n") self.lib.write(" map_only : true;\n") self.lib.write(" dont_touch : true;\n") - self.lib.write(" area : {0};\n\n".format(sram.width * sram.height)) - - times = self.sh.analyze() - - for i in times.keys(): - times[i] = ch.round_time(times[i]) - - - probe_address = "1" * self.addr_size - probe_data = self.word_size - 1 - - if use_model: - data = sram.analytical_model(slope=0.001) - else: - data = self.d.analyze(probe_address, probe_data) - - for i in data.keys(): - if i == "read_power" or i == "write_power": - continue - data[i] = ch.round_time(data[i]) - - - self.write_data_bus(data, times) - self.write_addr_bus(times) - self.write_control_pins(times) - self.write_clk(data) - - self.lib.close() + self.lib.write(" area : {0};\n\n".format(self.sram.width * self.sram.height)) def write_units(self): @@ -116,47 +119,75 @@ class lib: self.lib.write(" default_fanout_load : 1.0 ;\n") self.lib.write(" default_max_fanout : 4.0 ;\n") self.lib.write(" default_connection_class : universal ;\n\n") + + def create_list(self,values): + """ Helper function to create quoted, line wrapped list """ + list_values = ", ".join(str(v) for v in values) + return "\"{0}\"".format(list_values) + + def create_array(self,values, length): + """ Helper function to create quoted, line wrapped array with each row of given length """ + # check that the length is a multiple or give an error! + debug.check(len(values)%length == 0,"Values are not a multiple of the length. Cannot make a full array.") + rounded_values = map(ch.round_time,values) + split_values = [rounded_values[i:i+length] for i in range(0, len(rounded_values), length)] + formatted_rows = map(self.create_list,split_values) + formatted_array = ",\\\n".join(formatted_rows) + return formatted_array + + def write_index(self, number, values): + """ Write the index """ + quoted_string = self.create_list(values) + self.lib.write(" index_{0}({1});\n".format(number,quoted_string)) + + def write_values(self, values, row_length, indent): + """ Write the index """ + quoted_string = self.create_array(values, row_length) + # indent each newline plus extra spaces for word values + indented_string = quoted_string.replace('\n', '\n' + indent +" ") + self.lib.write("{0}values({1});\n".format(indent,indented_string)) - def write_LUT(self): + def write_LUT_templates(self): """ Adds lookup_table format (A 1x1 lookup_table).""" - Tran = ["CELL_UP_FOR_CLOCK" , "CELL_DN_FOR_CLOCK"] + Tran = ["CELL_TABLE"] for i in Tran: self.lib.write(" lu_table_template({0})".format(i)) self.lib.write("{\n") self.lib.write(" variable_1 : input_net_transition;\n") self.lib.write(" variable_2 : total_output_net_capacitance;\n") - self.lib.write(" index_1 (\"0.5\");\n") - self.lib.write(" index_2 (\"0.5\");\n") + self.write_index(1,self.slews) + self.write_index(2,self.loads) self.lib.write(" }\n\n") - CONS = ["CONSTRAINT_HIGH_POS" , "CONSTRAINT_LOW_POS"] + CONS = ["CONSTRAINT_TABLE"] for i in CONS: self.lib.write(" lu_table_template({0})".format(i)) self.lib.write("{\n") self.lib.write(" variable_1 : related_pin_transition;\n") self.lib.write(" variable_2 : constrained_pin_transition;\n") - self.lib.write(" index_1 (\"0.5\");\n") - self.lib.write(" index_2 (\"0.5\");\n") + self.write_index(1,self.slews) + self.write_index(2,self.slews) self.lib.write(" }\n\n") - self.lib.write(" lu_table_template(CLK_TRAN) {\n") - self.lib.write(" variable_1 : constrained_pin_transition;\n") - self.lib.write(" index_1 (\"0.5\");\n") - self.lib.write(" }\n\n") + # self.lib.write(" lu_table_template(CLK_TRAN) {\n") + # self.lib.write(" variable_1 : constrained_pin_transition;\n") + # self.write_index(1,self.slews) + # self.lib.write(" }\n\n") - self.lib.write(" lu_table_template(TRAN) {\n") - self.lib.write(" variable_1 : total_output_net_capacitance;\n") - self.lib.write(" index_1 (\"0.5\");\n") - self.lib.write(" }\n\n") + # self.lib.write(" lu_table_template(TRAN) {\n") + # self.lib.write(" variable_1 : total_output_net_capacitance;\n") + # self.write_index(1,self.slews) + # self.lib.write(" }\n\n") - CONS2 = ["INPUT_BY_TRANS_FOR_CLOCK" , "INPUT_BY_TRANS_FOR_SIGNAL"] - for i in CONS2: - self.lib.write(" power_lut_template({0})".format(i)) - self.lib.write("{\n") - self.lib.write(" variable_1 : input_transition_time;\n") - self.lib.write(" index_1 (\"0.5\");\n") - self.lib.write(" }\n\n") + # CONS2 = ["INPUT_BY_TRANS_FOR_CLOCK" , "INPUT_BY_TRANS_FOR_SIGNAL"] + # for i in CONS2: + # self.lib.write(" power_lut_template({0})".format(i)) + # self.lib.write("{\n") + # self.lib.write(" variable_1 : input_transition_time;\n") + # #self.write_index(1,self.slews) + # self.write_index(1,[self.slews[0]]) + # self.lib.write(" }\n\n") def write_bus(self): """ Adds format of DATA and ADDR bus.""" @@ -179,107 +210,122 @@ class lib: self.lib.write(" }\n\n") - def write_timing(self, times): + def write_FF_setuphold(self): """ Adds Setup and Hold timing results""" + self.compute_setup_hold() + self.lib.write(" timing(){ \n") self.lib.write(" timing_type : setup_rising; \n") self.lib.write(" related_pin : \"clk\"; \n") - self.lib.write(" rise_constraint(CONSTRAINT_HIGH_POS) {\n") - self.lib.write(" values(\"{0}\"); \n".format(times["setup_time_one"])) + self.lib.write(" rise_constraint(CONSTRAINT_TABLE) {\n") + rounded_values = map(ch.round_time,self.times["setup_times_LH"]) + self.write_values(rounded_values,len(self.slews)," ") self.lib.write(" }\n") - self.lib.write(" fall_constraint(CONSTRAINT_LOW_POS) {\n") - self.lib.write(" values(\"{0}\"); \n".format(times["setup_time_zero"])) + self.lib.write(" fall_constraint(CONSTRAINT_TABLE) {\n") + rounded_values = map(ch.round_time,self.times["setup_times_HL"]) + self.write_values(rounded_values,len(self.slews)," ") self.lib.write(" }\n") self.lib.write(" }\n") self.lib.write(" timing(){ \n") self.lib.write(" timing_type : hold_rising; \n") self.lib.write(" related_pin : \"clk\"; \n") - self.lib.write(" rise_constraint(CONSTRAINT_HIGH_POS) {\n") - self.lib.write(" values(\"{0}\"); \n".format(times["hold_time_one"])) + self.lib.write(" rise_constraint(CONSTRAINT_TABLE) {\n") + rounded_values = map(ch.round_time,self.times["hold_times_LH"]) + self.write_values(rounded_values,len(self.slews)," ") self.lib.write(" }\n") - self.lib.write(" fall_constraint(CONSTRAINT_LOW_POS) {\n") - self.lib.write(" values(\"{0}\"); \n".format(times["hold_time_zero"])) + self.lib.write(" fall_constraint(CONSTRAINT_TABLE) {\n") + rounded_values = map(ch.round_time,self.times["hold_times_HL"]) + self.write_values(rounded_values,len(self.slews)," ") self.lib.write(" }\n") self.lib.write(" }\n") - def write_data_bus(self, data, times): + def write_data_bus(self): """ Adds data bus timing results.""" + + self.compute_delay() + self.lib.write(" bus(DATA){\n") self.lib.write(" bus_type : DATA; \n") self.lib.write(" direction : inout; \n") - self.lib.write(" max_capacitance : {0}; \n".format(tech.spice["FF_in_cap"] + tech.spice["tri_gate_out_cap"] )) - self.lib.write(" pin(DATA[{0}:0])".format(self.word_size - 1)) - self.lib.write("{\n") - self.lib.write(" }\n") + self.lib.write(" max_capacitance : {0}; \n".format(8*tech.spice["FF_in_cap"])) 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(" internal_power(){\n") - self.lib.write(" when : \"OEb & !clk\"; \n") - self.lib.write(" rise_power(INPUT_BY_TRANS_FOR_SIGNAL){\n") - self.lib.write(" values(\"{0}\");\n".format(data["write_power"]* 1e3)) - self.lib.write(" }\n") - self.lib.write(" fall_power(INPUT_BY_TRANS_FOR_SIGNAL){\n") - self.lib.write(" values(\"{0}\");\n".format(data["write_power"]* 1e3)) - self.lib.write(" }\n") - self.lib.write(" }\n") - self.write_timing(times) - self.lib.write(" memory_read(){ \n") self.lib.write(" address : ADDR; \n") self.lib.write(" }\n") + self.lib.write(" pin(DATA[{0}:0])".format(self.word_size - 1)) + self.lib.write("{\n") + + self.lib.write(" internal_power(){\n") + self.lib.write(" when : \"OEb & !clk\"; \n") + self.lib.write(" rise_power(scalar){\n") + self.lib.write(" values(\"{0}\");\n".format(self.delay["write1_power"])) + self.lib.write(" }\n") + self.lib.write(" fall_power(scalar){\n") + self.lib.write(" values(\"{0}\");\n".format(self.delay["write0_power"])) + self.lib.write(" }\n") + self.lib.write(" }\n") + + self.write_FF_setuphold() + self.lib.write(" internal_power(){\n") self.lib.write(" when : \"!OEb & !clk\"; \n") - self.lib.write(" rise_power(INPUT_BY_TRANS_FOR_SIGNAL){\n") - self.lib.write(" values(\"{0}\");\n".format(data["read_power"]* 1e3)) + self.lib.write(" rise_power(scalar){\n") + self.lib.write(" values(\"{0}\");\n".format(self.delay["read1_power"])) self.lib.write(" }\n") - self.lib.write(" fall_power(INPUT_BY_TRANS_FOR_SIGNAL){\n") - self.lib.write(" values(\"{0}\");\n".format(data["read_power"]* 1e3)) + self.lib.write(" fall_power(scalar){\n") + self.lib.write(" values(\"{0}\");\n".format(self.delay["read0_power"])) self.lib.write(" }\n") self.lib.write(" }\n") 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 : rising_edge; \n") - self.lib.write(" cell_rise(CELL_UP_FOR_CLOCK) {\n") - self.lib.write(" values(\"{0}\"); \n".format(data["delay1"])) + self.lib.write(" timing_type : falling_edge; \n") + self.lib.write(" cell_rise(CELL_TABLE) {\n") + rounded_values = map(ch.round_time,self.delay["delay1"]) + self.write_values(rounded_values,len(self.loads)," ") self.lib.write(" }\n") - self.lib.write(" cell_fall(CELL_DN_FOR_CLOCK) {\n") - self.lib.write(" values(\"{0}\"); \n".format(data["delay0"])) + self.lib.write(" cell_fall(CELL_TABLE) {\n") + rounded_values = map(ch.round_time,self.delay["delay0"]) + self.write_values(rounded_values,len(self.loads)," ") self.lib.write(" }\n") - self.lib.write(" rise_transition(TRAN) {\n") - self.lib.write(" values(\"{0}\"); \n".format(data["delay1"])) + self.lib.write(" rise_transition(CELL_TABLE) {\n") + rounded_values = map(ch.round_time,self.delay["slew1"]) + self.write_values(rounded_values,len(self.loads)," ") self.lib.write(" }\n") - self.lib.write(" fall_transition(TRAN) {\n") - self.lib.write(" values(\"{0}\"); \n".format(data["delay0"])) + self.lib.write(" fall_transition(CELL_TABLE) {\n") + rounded_values = map(ch.round_time,self.delay["slew0"]) + self.write_values(rounded_values,len(self.loads)," ") self.lib.write(" }\n") self.lib.write(" }\n") + self.lib.write(" }\n") self.lib.write(" }\n\n") - def write_addr_bus(self, times): + def write_addr_bus(self): """ Adds addr bus timing results.""" self.lib.write(" bus(ADDR){\n") self.lib.write(" bus_type : ADDR; \n") self.lib.write(" direction : input; \n") self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"])) - self.lib.write(" max_transition : 0.5;\n") + self.lib.write(" max_transition : {0};\n".format(self.slews[-1])) self.lib.write(" fanout_load : 1.000000;\n") self.lib.write(" pin(ADDR[{0}:0])".format(self.addr_size - 1)) self.lib.write("{\n") - self.lib.write(" }\n") - self.write_timing(times) + + self.write_FF_setuphold() + self.lib.write(" }\n") self.lib.write(" }\n\n") - def write_control_pins(self, times): + def write_control_pins(self): """ Adds control pins timing results.""" ctrl_pin_names = ["CSb", "OEb", "WEb"] @@ -288,39 +334,68 @@ class lib: self.lib.write("{\n") self.lib.write(" direction : input; \n") self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"])) - self.write_timing(times) + self.write_FF_setuphold() self.lib.write(" }\n\n") - def write_clk(self, data): + def write_clk(self): """ Adds clk pin timing results.""" + + self.compute_delay() self.lib.write(" pin(clk){\n") self.lib.write(" clock : true;\n") self.lib.write(" direction : input; \n") self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"])) - min_pulse_width = (ch.round_time(data["min_period1"]) + ch.round_time(data["min_period0"]))/2.0 - min_period = ch.round_time(data["min_period1"]) + ch.round_time(data["min_period0"]) + min_pulse_width = ch.round_time(self.delay["min_period"])/2.0 + min_period = ch.round_time(self.delay["min_period"]) self.lib.write(" timing(){ \n") self.lib.write(" timing_type :\"min_pulse_width\"; \n") self.lib.write(" related_pin : clk; \n") - self.lib.write(" rise_constraint(CLK_TRAN) {\n") + self.lib.write(" rise_constraint(scalar) {\n") self.lib.write(" values(\"{0}\"); \n".format(min_pulse_width)) self.lib.write(" }\n") - self.lib.write(" fall_constraint(CLK_TRAN) {\n") + self.lib.write(" fall_constraint(scalar) {\n") self.lib.write(" values(\"{0}\"); \n".format(min_pulse_width)) self.lib.write(" }\n") self.lib.write(" }\n") self.lib.write(" timing(){ \n") self.lib.write(" timing_type :\"minimum_period\"; \n") self.lib.write(" related_pin : clk; \n") - self.lib.write(" rise_constraint(CLK_TRAN) {\n") + self.lib.write(" rise_constraint(scalar) {\n") self.lib.write(" values(\"{0}\"); \n".format(min_period)) self.lib.write(" }\n") - self.lib.write(" fall_constraint(CLK_TRAN) {\n") + self.lib.write(" fall_constraint(scalar) {\n") self.lib.write(" values(\"{0}\"); \n".format(min_period)) self.lib.write(" }\n") self.lib.write(" }\n") self.lib.write(" }\n") self.lib.write(" }\n") self.lib.write("}\n") + + def compute_delay(self): + """ Do the analysis if we haven't characterized the SRAM yet """ + try: + self.d + except AttributeError: + self.d = delay.delay(self.sram, self.spfile) + probe_address = "1" * self.addr_size + probe_data = self.word_size - 1 + if self.use_model: + self.d = True + self.delay = self.sram.analytical_model(self.slews,self.loads) + else: + self.delay = self.d.analyze(probe_address, probe_data, self.slews, self.loads) + + def compute_setup_hold(self): + """ Do the analysis if we haven't characterized a FF yet """ + # Do the analysis if we haven't characterized a FF yet + try: + self.sh + except AttributeError: + self.sh = setup_hold.setup_hold() + if self.use_model: + self.times = self.sh.analytical_model(self.slews,self.loads) + else: + self.times = self.sh.analyze(self.slews,self.slews) + diff --git a/compiler/characterizer/setup_hold.py b/compiler/characterizer/setup_hold.py index 999a3a95..e0c43ffd 100644 --- a/compiler/characterizer/setup_hold.py +++ b/compiler/characterizer/setup_hold.py @@ -8,9 +8,6 @@ import ms_flop OPTS = globals.get_opts() -vdd = tech.spice["supply_voltage"] -gnd = tech.spice["gnd_voltage"] - class setup_hold(): """ @@ -20,311 +17,308 @@ class setup_hold(): def __init__(self): # This must match the spice model order - self.pins = ["data_buf", "dout", "dout_bar", "clk_buf", "vdd", "gnd"] - self.output_name = "dout" + self.pins = ["data", "dout", "dout_bar", "clk", "vdd", "gnd"] self.model_name = "ms_flop" self.model_location = OPTS.openram_tech + "sp_lib/ms_flop.sp" + self.period = tech.spice["feasible_period"] + self.vdd = tech.spice["supply_voltage"] + self.gnd = tech.spice["gnd_voltage"] + debug.info(2,"Feasible period from technology file: {0} ".format(self.period)) - def check_arguments(self, correct_value, period): - """Checks if given arguments for write_stimulus() meets requirements""" - if not isinstance(correct_value, float): - if not isinstance(correct_value, int): - debug.error("Given correct_value is not a valid number",1) - if not isinstance(period, float): - if not isinstance(period, int): - debug.error("Given period is not a valid number",1) - def write_stimulus(self, mode, target_time, correct_value, period, noise_margin): + def write_stimulus(self, mode, target_time, correct_value): """Creates a stimulus file for SRAM setup/hold time calculation""" - self.check_arguments(correct_value,period) # creates and opens the stimulus file for writing temp_stim = OPTS.openram_temp + "stim.sp" self.sf = open(temp_stim, "w") - self.write_header(correct_value, period) + self.write_header(correct_value) # instantiate the master-slave d-flip-flop self.sf.write("* Instantiation of the Master-Slave D-flip-flop\n") stimuli.inst_model(stim_file=self.sf, pins=self.pins, model_name=self.model_name) - self.sf.write("\n") - - # create a buffer for the inputs - self.sf.write("* Buffer subckt\n") - stimuli.create_buffer(stim_file=self.sf, - buffer_name="buffer", - size=[1, 1]) - self.sf.write("\n") self.write_data(mode=mode, target_time=target_time, - period=period, correct_value=correct_value) - self.write_clock(period) + self.write_clock() self.write_measures(mode=mode, - correct_value=correct_value, - noise_margin=noise_margin, - period=period) + correct_value=correct_value) + - self.write_control(period=period) + stimuli.write_control(self.sf,4*self.period) self.sf.close() - def write_header(self, correct_value, period): + def write_header(self, correct_value): """ Write the header file with all the models and the power supplies. """ - self.sf.write("* Stimulus for setup/hold: data {0} period {1}n\n".format(correct_value, period)) - self.sf.write("\n") + self.sf.write("* Stimulus for setup/hold: data {0} period {1}n\n".format(correct_value, self.period)) # include files in stimulus file self.model_list = tech.spice["fet_models"] + [self.model_location] stimuli.write_include(stim_file=self.sf, models=self.model_list) - self.sf.write("\n") # add vdd/gnd statements self.sf.write("* Global Power Supplies\n") - stimuli.write_supply(stim_file=self.sf, - vdd_name=tech.spice["vdd_name"], - gnd_name=tech.spice["gnd_name"], - vdd_voltage=vdd, - gnd_voltage=gnd) - self.sf.write("\n") + stimuli.write_supply(self.sf) - def write_data(self, mode, period, target_time, correct_value): - """ Create the buffered data signals for setup/hold analysis """ - self.sf.write("* Buffer for the DATA signal\n") - stimuli.add_buffer(stim_file=self.sf, - buffer_name="buffer", - signal_list=["DATA"]) + def write_data(self, mode, target_time, correct_value): + """Create the data signals for setup/hold analysis. First period is to + initialize it to the opposite polarity. Second period is used for + characterization. + + """ self.sf.write("* Generation of the data and clk signals\n") incorrect_value = stimuli.get_inverse_value(correct_value) if mode=="HOLD": + init_value = incorrect_value start_value = correct_value end_value = incorrect_value else: + init_value = incorrect_value start_value = incorrect_value end_value = correct_value - stimuli.gen_pulse(stim_file=self.sf, - sig_name="DATA", - v1=start_value, - v2=end_value, - offset=target_time, - period=2*period, - t_rise=tech.spice["rise_time"], - t_fall=tech.spice["fall_time"]) - self.sf.write("\n") + stimuli.gen_pwl(stim_file=self.sf, + sig_name="data", + clk_times=[self.period, target_time], + data_values=[init_value, start_value, end_value], + period=target_time, + slew=self.constrained_input_slew, + setup=0) - def write_clock(self,period): - """ Create the buffered clock signal for setup/hold analysis """ - self.sf.write("* Buffer for the clk signal\n") - stimuli.add_buffer(stim_file=self.sf, - buffer_name="buffer", - signal_list=["clk"]) - self.sf.write("\n") - stimuli.gen_pulse(stim_file=self.sf, - sig_name="clk", - offset=period, - period=period, - t_rise=tech.spice["rise_time"], - t_fall=tech.spice["fall_time"]) - self.sf.write("\n") + def write_clock(self): + """ Create the clock signal for setup/hold analysis. First period initializes the FF + while the second is used for characterization.""" + + stimuli.gen_pwl(stim_file=self.sf, + sig_name="clk", + # initial clk edge is right after the 0 time to initialize a flop + # without using .IC on an internal node. + # Return input to value after one period. + # The second pulse is the characterization one at 2*period + clk_times=[0.1*self.period,self.period,2*self.period], + data_values=[0, 1, 0, 1], + period=2*self.period, + slew=self.constrained_input_slew, + setup=0) - def write_measures(self, mode, correct_value, noise_margin, period): + + def write_measures(self, mode, correct_value): """ Measure statements for setup/hold with right phases. """ - if correct_value == vdd: - max_or_min = "MAX" - rise_or_fall = "RISE" + if correct_value == 1: + dout_rise_or_fall = "RISE" else: - max_or_min = "MIN" - rise_or_fall = "FALL" + dout_rise_or_fall = "FALL" + + # in SETUP mode, the input mirrors what the output should be + if mode == "SETUP": + din_rise_or_fall = dout_rise_or_fall + else: + # in HOLD mode, however, the input should be opposite of the output + if correct_value == 1: + din_rise_or_fall = "FALL" + else: + din_rise_or_fall = "RISE" - incorrect_value = stimuli.get_inverse_value(correct_value) self.sf.write("* Measure statements for pass/fail verification\n") - self.sf.write(".IC v({0})={1}\n".format(self.output_name, incorrect_value)) - #self.sf.write(".MEASURE TRAN {0}VOUT {0} v({1}) GOAL={2}\n".format(max_or_min, output_name, noise_margin)) - # above is the old cmd for hspice, below is the one work for both - self.sf.write(".MEASURE TRAN {0}VOUT {0} v({1}) from ={2}n to ={3}n\n".format(max_or_min, - self.output_name, - 1.5*period, - 2*period)) - self.sf.write("\n") + trig_name = "clk" + targ_name = "dout" + trig_val = targ_val = 0.5 * self.vdd + # Start triggers right before the clock edge at 2*period + stimuli.gen_meas_delay(stim_file=self.sf, + meas_name="clk2q_delay", + trig_name=trig_name, + targ_name=targ_name, + trig_val=trig_val, + targ_val=targ_val, + trig_dir="RISE", + targ_dir=dout_rise_or_fall, + td=1.9*self.period) + + targ_name = "data" + # Start triggers right after initialize value is returned to normal + # at one period + stimuli.gen_meas_delay(stim_file=self.sf, + meas_name="setup_hold_time", + trig_name=trig_name, + targ_name=targ_name, + trig_val=trig_val, + targ_val=targ_val, + trig_dir="RISE", + targ_dir=din_rise_or_fall, + td=1.2*self.period) + - def write_control(self, period): - # transient window - end_time = 2 * period - self.sf.write(".TRAN 5p {0}n\n".format(end_time)) - self.sf.write(".OPTIONS POST=1 PROBE\n") - # create plots for all signals - self.sf.write(".probe V(*)\n") - # end the stimulus file - self.sf.write(".end\n") - def bidir_search(self, correct_value, noise_margin, measure_name, mode): + def bidir_search(self, correct_value, mode): """ This will perform a bidirectional search for either setup or hold times. It starts with the feasible priod and looks a half period beyond or before it depending on whether we are doing setup or hold. """ - period = tech.spice["feasible_period"] - debug.info(2,"Feasible period from technology file: {0} ".format(period)) - - - # The clock will start being offset by a period, so we want to look before and after - # this time by half a period. - if mode == "HOLD": - target_time = 1.5 * period - lower_bound = 0.5*period - upper_bound = 1.5 * period - else: - target_time = 0.5 * period - lower_bound = 0.5 * period - upper_bound = 1.5*period - previous_time = target_time - # Initial Check if reference setup time passes for correct_value + # NOTE: The feasible bound is always feasible. This is why they are different for setup and hold. + # The clock will always be offset by 2*period from the start, so we want to look before and after + # this time. They are also unbalanced so that the average won't be right on the clock edge in the + # first iteration. + if mode == "SETUP": + feasible_bound = 1.25*self.period + infeasible_bound = 2.5*self.period + else: + infeasible_bound = 1.5*self.period + feasible_bound = 2.75*self.period + + # Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search! self.write_stimulus(mode=mode, - target_time=target_time, - correct_value=correct_value, - period=period, - noise_margin=noise_margin) + target_time=feasible_bound, + correct_value=correct_value) stimuli.run_sim() - output_value = ch.convert_to_float(ch.parse_output("timing", measure_name)) - debug.info(3,"Correct: {0} Output: {1} NM: {2}".format(correct_value,output_value,noise_margin)) - if mode == "HOLD": - setuphold_time = target_time - period - else: - setuphold_time = period - target_time - debug.info(2,"Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,setuphold_time, - target_time,period)) - debug.info(3,"Target time: {0} Low: {1} Up: {2} Measured: {3}".format(target_time, - lower_bound, - upper_bound, - setuphold_time)) - if not self.pass_fail_test(output_value, correct_value, noise_margin): - debug.error("Initial period/target hold time fails for data value",2) + ideal_clk_to_q = ch.convert_to_float(ch.parse_output("timing", "clk2q_delay")) + setuphold_time = ch.convert_to_float(ch.parse_output("timing", "setup_hold_time")) + debug.info(2,"*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, correct_value,ideal_clk_to_q,setuphold_time)) - # We already found it feasible, so advance one step first thing. - debug.info(2,"Performing bidir search on {3} time: {2} LB: {0} UB: {1} ".format(lower_bound, - upper_bound, - setuphold_time, - mode)) - if mode == "HOLD": - target_time -= 0.5 * (upper_bound - lower_bound) + if type(ideal_clk_to_q)!=float or type(setuphold_time)!=float: + debug.error("Initial hold time fails for data value feasible bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound,ideal_clk_to_q,setuphold_time),2) + + if mode == "SETUP": # SETUP is clk-din, not din-clk + setuphold_time *= -1e9 else: - target_time += 0.5 * (upper_bound - lower_bound) + setuphold_time *= 1e9 + + passing_setuphold_time = setuphold_time + debug.info(2,"Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode, + setuphold_time, + feasible_bound, + 2*self.period)) + #raw_input("Press Enter to continue...") + while True: + target_time = (feasible_bound + infeasible_bound)/2 self.write_stimulus(mode=mode, target_time=target_time, - correct_value=correct_value, - period=period, - noise_margin=noise_margin) - if mode == "HOLD": - setuphold_time = target_time - period - else: - setuphold_time = period - target_time - debug.info(3,"Target time: {0} Low: {1} Up: {2} Measured: {3}".format(target_time, - lower_bound, - upper_bound, - setuphold_time)) + correct_value=correct_value) + + debug.info(2,"{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode, + correct_value, + target_time, + infeasible_bound, + feasible_bound)) + stimuli.run_sim() - output_value = ch.convert_to_float(ch.parse_output("timing", measure_name)) - debug.info(3,"Correct: {0} Output: {1} NM: {2}".format(correct_value,output_value,noise_margin)) - if self.pass_fail_test(output_value,correct_value,noise_margin): - debug.info(3,"PASS") - if ch.relative_compare(target_time, previous_time): - debug.info(3,"CONVERGE " + str(target_time) + " " + str(previous_time)) - break - previous_time = target_time - if mode == "HOLD": - upper_bound = target_time - target_time -= 0.5 * (upper_bound - lower_bound) + clk_to_q = ch.convert_to_float(ch.parse_output("timing", "clk2q_delay")) + setuphold_time = ch.convert_to_float(ch.parse_output("timing", "setup_hold_time")) + if type(clk_to_q)==float and (clk_to_q<1.1*ideal_clk_to_q) and type(setuphold_time)==float: + if mode == "SETUP": # SETUP is clk-din, not din-clk + setuphold_time *= -1e9 else: - lower_bound = target_time - target_time += 0.5 * (upper_bound - lower_bound) + setuphold_time *= 1e9 + + debug.info(2,"PASS Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q,setuphold_time)) + passing_setuphold_time = setuphold_time + feasible_bound = target_time else: - debug.info(3,"FAIL") - if mode == "HOLD": - lower_bound = target_time - target_time += 0.5 * (upper_bound - lower_bound) - else: - upper_bound = target_time - target_time -= 0.5 * (upper_bound - lower_bound) + debug.info(2,"FAIL Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q,setuphold_time)) + infeasible_bound = target_time + #raw_input("Press Enter to continue...") - # the clock starts offset by one clock period, - # so we always measure our setup or hold relative to this time - if mode == "HOLD": - setuphold_time = target_time - period - else: - setuphold_time = period - target_time + if ch.relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001): + debug.info(3,"CONVERGE {0} vs {1}".format(feasible_bound,infeasible_bound)) + break + - debug.info(2,"Converged on {0} time {1}, data at {2}, clock at {3}.".format(mode,setuphold_time,target_time,period)) - return setuphold_time + debug.info(2,"Converged on {0} time {1}.".format(mode,passing_setuphold_time)) + return passing_setuphold_time - def setup_time(self): - """Calculates the setup time for low-to-high and high-to-low - transition for a D-flip-flop""" - - one_found = self.bidir_search(vdd, 0.9*vdd, "maxvout", "SETUP") - - zero_found = self.bidir_search(gnd, 0.1*vdd, "minvout", "SETUP") - - return [one_found, zero_found] - - def hold_time(self): - """Calculates the hold time for low-to-high and high-to-low - transition for a D-flip-flop""" - - one_found = self.bidir_search(vdd, 0.9*vdd, "maxvout", "HOLD") - - zero_found = self.bidir_search(gnd, 0.1*vdd, "minvout", "HOLD") - - return [one_found, zero_found] - - - def pass_fail_test(self,value,correct_value,noise_margin): - """Function to Test if the output value reached the - noise_margin to determine if it passed or failed""" - if correct_value == vdd: - return True if value >= noise_margin else False - else: - return True if value <= noise_margin else False - - - - - def analyze(self): - """main function to calculate both setup and hold time for the - d-flip-flop returns a dictionary that contains 4 times for both - setup/hold times for high_to_low and low_to_high transition - dictionary keys: setup_time_one (low_to_high), setup_time_zero - (high_to_low), hold_time_one (low_to_high), hold_time_zero - (high_to_low) + def setup_LH_time(self): + """Calculates the setup time for low-to-high transition for a DFF """ + return self.bidir_search(1, "SETUP") - [one_setup_time, zero_setup_time] = self.setup_time() - [one_hold_time, zero_hold_time] = self.hold_time() - debug.info(1, "Setup Time for low_to_high transistion: {0}".format(one_setup_time)) - debug.info(1, "Setup Time for high_to_low transistion: {0}".format(zero_setup_time)) - debug.info(1, "Hold Time for low_to_high transistion: {0}".format(one_hold_time)) - debug.info(1, "Hold Time for high_to_low transistion: {0}".format(zero_hold_time)) - times = {"setup_time_one": one_setup_time, - "setup_time_zero": zero_setup_time, - "hold_time_one": one_hold_time, - "hold_time_zero": zero_hold_time + + def setup_HL_time(self): + """Calculates the setup time for high-to-low transition for a DFF + """ + return self.bidir_search(0, "SETUP") + + def hold_LH_time(self): + """Calculates the hold time for low-to-high transition for a DFF + """ + return self.bidir_search(1, "HOLD") + + def hold_HL_time(self): + """Calculates the hold time for high-to-low transition for a DFF + """ + return self.bidir_search(0, "HOLD") + + + def analyze(self, related_slews, constrained_slews): + """main function to calculate both setup and hold time for the + DFF and returns a dictionary that contains 4 lists for both + setup/hold times for high_to_low and low_to_high transitions + for all the slew combinations of the data and clock. + """ + LH_setup = [] + HL_setup = [] + LH_hold = [] + HL_hold = [] + for self.related_input_slew in related_slews: + for self.constrained_input_slew in constrained_slews: + debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,self.constrained_input_slew)) + LH_setup_time = self.setup_LH_time() + debug.info(1, " Setup Time for low_to_high transistion: {0}".format(LH_setup_time)) + HL_setup_time = self.setup_HL_time() + debug.info(1, " Setup Time for high_to_low transistion: {0}".format(HL_setup_time)) + LH_hold_time = self.hold_LH_time() + debug.info(1, " Hold Time for low_to_high transistion: {0}".format(LH_hold_time)) + HL_hold_time = self.hold_HL_time() + debug.info(1, " Hold Time for high_to_low transistion: {0}".format(HL_hold_time)) + LH_setup.append(LH_setup_time) + HL_setup.append(HL_setup_time) + LH_hold.append(LH_hold_time) + HL_hold.append(HL_hold_time) + + times = {"setup_times_LH": LH_setup, + "setup_times_HL": HL_setup, + "hold_times_LH": LH_hold, + "hold_times_HL": HL_hold } return times + def analytical_model(self,related_slews, constrained_slews): + """ Just return the fixed setup/hold times from the technology. + """ + LH_setup = [] + HL_setup = [] + LH_hold = [] + HL_hold = [] + + for self.related_input_slew in related_slews: + for self.constrained_input_slew in constrained_slews: + # convert from ps to ns + LH_setup.append(tech.spice["msflop_setup"]/1e3) + HL_setup.append(tech.spice["msflop_setup"]/1e3) + LH_hold.append(tech.spice["msflop_hold"]/1e3) + HL_hold.append(tech.spice["msflop_hold"]/1e3) + + times = {"setup_times_LH": LH_setup, + "setup_times_HL": HL_setup, + "hold_times_LH": LH_hold, + "hold_times_HL": HL_hold + } + return times diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 16db432c..509a6224 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -10,11 +10,12 @@ import debug import subprocess import os import sys +import numpy as np OPTS = globals.get_opts() -vdd = tech.spice["supply_voltage"] -gnd = tech.spice["gnd_voltage"] +vdd_voltage = tech.spice["supply_voltage"] +gnd_voltage = tech.spice["gnd_voltage"] vdd_name = tech.spice["vdd_name"] gnd_name = tech.spice["gnd_name"] pmos_name = tech.spice["pmos_name"] @@ -26,14 +27,14 @@ def inst_sram(stim_file, abits, dbits, sram_name): """function to instatiate the sram subckt""" stim_file.write("Xsram ") for i in range(dbits): - stim_file.write("DATA[{0}] ".format(i)) + stim_file.write("D[{0}] ".format(i)) for i in range(abits): - stim_file.write("A[{0}]_buf ".format(i)) + stim_file.write("A[{0}] ".format(i)) for i in tech.spice["control_signals"]: - stim_file.write("{0}_buf ".format(i)) - stim_file.write("{0}_buf_buf ".format(tech.spice["clk"])) + stim_file.write("{0} ".format(i)) + stim_file.write("{0} ".format(tech.spice["clk"])) stim_file.write("{0} {1} ".format(vdd_name, gnd_name)) - stim_file.write("{0}\n".format(sram_name)) + stim_file.write("{0}\n\n".format(sram_name)) def inst_model(stim_file, pins, model_name): @@ -82,10 +83,10 @@ def create_buffer(stim_file, buffer_name, size=[1,3], beta=2.5): nmos_name, size[1] * tx_width, tx_length)) - stim_file.write(".ENDS test_{0}\n".format(buffer_name)) + stim_file.write(".ENDS test_{0}\n\n".format(buffer_name)) -def add_buffer(stim_file, buffer_name, signal_list): +def inst_buffer(stim_file, buffer_name, signal_list): """Adds buffers to each top level signal that is in signal_list (only for sim purposes)""" for signal in signal_list: stim_file.write("X{0}_buffer {0} {0}_buf {1} {2} test_{3}\n".format(signal, @@ -94,7 +95,7 @@ def add_buffer(stim_file, buffer_name, signal_list): buffer_name)) -def add_inverter(stim_file, signal_list): +def inst_inverter(stim_file, signal_list): """Adds inv for each signal that needs its inverted version (only for sim purposes)""" for signal in signal_list: stim_file.write("X{0}_inv {0} {0}_inv {1} {2} test_inv\n".format(signal, @@ -102,24 +103,24 @@ def add_inverter(stim_file, signal_list): "test"+gnd_name)) -def add_accesstx(stim_file, dbits): +def inst_accesstx(stim_file, dbits): """Adds transmission gate for inputs to data-bus (only for sim purposes)""" stim_file.write("* Tx Pin-list: Drain Gate Source Body\n") for i in range(dbits): - pmos_access_string="mp{0} DATA[{0}] WEb_trans_buf D[{0}]_buf {1} {2} w={3}u l={4}u\n" + pmos_access_string="mp{0} DATA[{0}] acc_en D[{0}] {1} {2} w={3}u l={4}u\n" stim_file.write(pmos_access_string.format(i, "test"+vdd_name, pmos_name, 2 * tx_width, tx_length)) - nmos_access_string="mn{0} DATA[{0}] WEb_trans_inv D[{0}]_buf {1} {2} w={3}u l={4}u\n" + nmos_access_string="mn{0} DATA[{0}] acc_en_inv D[{0}] {1} {2} w={3}u l={4}u\n" stim_file.write(nmos_access_string.format(i, "test"+gnd_name, nmos_name, 2 * tx_width, tx_length)) -def gen_pulse(stim_file, sig_name, v1=gnd, v2=vdd, offset=0, period=1, t_rise=0, t_fall=0): +def gen_pulse(stim_file, sig_name, v1=gnd_voltage, v2=vdd_voltage, offset=0, period=1, t_rise=0, t_fall=0): """Generates a periodic signal with 50% duty cycle and slew rates. Period is measured from 50% to 50%.""" pulse_string="V{0} {0} 0 PULSE ({1} {2} {3}n {4}n {5}n {6}n {7}n)\n" @@ -133,276 +134,95 @@ def gen_pulse(stim_file, sig_name, v1=gnd, v2=vdd, offset=0, period=1, t_rise=0, period)) +def gen_pwl(stim_file, sig_name, clk_times, data_values, period, slew, setup): + # the initial value is not a clock time + debug.check(len(clk_times)+1==len(data_values),"Clock and data value lengths don't match.") + # shift signal times earlier for setup time + times = np.array(clk_times) - setup*period + values = np.array(data_values) * vdd_voltage + half_slew = 0.5 * slew + stim_file.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0])) + for i in range(len(times)): + stim_file.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew, + values[i], + times[i]+half_slew, + values[i+1])) + stim_file.write(")\n") -def gen_clk_pwl(stim_file, cycle_times, t_fall, t_rise): - """Generates a clk signal using pwl. The cycle times are the times of the - clock rising edge. It is assumed to start at time 0 with clock 0. Duty - cycle is assumed to be 50%. Rise/fall times are 0-100%.""" - stim_file.write("V{0} {0} 0 PWL (0n 0v ".format(tech.spice["clk"])) - for i in range(len(cycle_times)-1): - period = cycle_times[i+1] - cycle_times[i] - t_current = cycle_times[i] - 0.5*t_rise # 50% point is at cycle time - t_current2 = t_current + t_rise - # rising edge - stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, gnd, t_current2, vdd)) - t_current = t_current + 0.5*period - 0.5*t_fall # 50% point is at cycle time - t_current2 = t_current + t_fall - # falling edge - stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, vdd, t_current2, gnd)) - # end time doesn't need a rising edge - stim_file.write("{0}n {1}v)\n".format(cycle_times[-1], gnd)) - - -def gen_data_pwl(stim_file, key_times, sig_name, data_value, feasible_period, target_period, t_rise, t_fall): +def gen_data(stim_file, clk_times, sig_name, period, slew): """Generates the PWL data inputs for a simulation timing test.""" - data_value_invert = gnd if data_value == vdd else vdd - - t_current = 0.0 - stim_file.write("V{0} {0} 0 PWL ({1}n {2}v ".format(sig_name, t_current, data_value_invert)) - t_current = key_times[2] - 0.25 * target_period - t_current += (0.5 * target_period) # uses falling edge for ZBT mode - slew_time = t_rise if data_value_invert == gnd else t_fall - t_current2 = t_current + slew_time - stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, data_value_invert, t_current2, data_value)) - t_current = key_times[2] + 0.25 * target_period - t_current += (0.5 * target_period) # uses falling edge for ZBT mode - slew_time = t_rise if data_value == gnd else t_fall - t_current2 = t_current + slew_time - stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, data_value, t_current2, data_value_invert)) - t_current = key_times[5] + 0.25 * feasible_period - stim_file.write("{0}n {1}v)\n".format(t_current, data_value_invert)) + # values for NOP, W1, W0, W1, R0, 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, 0, 0, 0 ] + gen_pwl(stim_file, sig_name, clk_times, values, period, slew, 0.05) -def gen_addr_pwl(stim_file, key_times, addr, feasible_period, target_period, t_rise, t_fall): - """Generates the PWL for address inputs for a simulation timing test""" - # reverse string - reversed_addr = addr[::-1] - # inverts all bits in address using intermediate value of 2 - invert_addr = reversed_addr.replace('1', '2').replace('0', '1').replace('2', '0') +def gen_addr(stim_file, clk_times, addr, period, slew): + """Generates the address inputs for a simulation timing test. + One cycle is different to clear the bus + """ + + zero_values = [0, 0, 0, 1, 0, 0, 1, 0, 0 ] + ones_values = [1, 1, 1, 0, 1, 1, 0, 1, 1 ] + + for i in range(len(addr)): + sig_name = "A[{0}]".format(i) + if addr[i]==1: + gen_pwl(stim_file, sig_name, clk_times, ones_values, period, slew, 0.05) + else: + gen_pwl(stim_file, sig_name, clk_times, zero_values, period, slew, 0.05) - for i in range(len(reversed_addr)): - v_val = gnd if reversed_addr[i] == '0' else vdd - v_val_invert = gnd if invert_addr[i] == '0' else vdd - t_current = 0.0 - stim_file.write("V{0} {0} 0 PWL ({1}n {2}v ".format("A[{0}]".format(i), t_current, v_val)) - t_current = key_times[3] - 0.25 * target_period - slew_time = t_rise if v_val == gnd else t_fall - t_current2 = t_current + slew_time - stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, v_val, t_current2, v_val_invert)) - t_current = key_times[4] - 0.25 * target_period - slew_time = t_rise if v_val_invert == gnd else t_fall - t_current2 = t_current + slew_time - stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, v_val_invert, t_current2, v_val)) - t_current = key_times[5] + 0.25 * feasible_period - stim_file.write("{0}n {1}v)\n".format(t_current, v_val)) - - -def gen_constant(stim_file, sig_name, v_ref, v_val): +def gen_constant(stim_file, sig_name, v_val): """Generates a constant signal with reference voltage and the voltage value""" - stim_file.write("V{0} {0} {1} DC {2}\n".format(sig_name, v_ref, v_val)) + stim_file.write("V{0} {0} 0 DC {1}\n".format(sig_name, v_val)) -def gen_csb_pwl(key_times, feasible_period, target_period, t_rise, t_fall): - """Returns two lists for x,y coordinates for the generation of CSb pwl""" - t_current = 0.0 - x_list = [t_current] - y_list = [vdd] +def gen_csb(stim_file, clk_times, period, slew): + """ Generates the PWL CSb signal""" + # values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP + values = [1, 0, 0, 0, 0, 0, 0, 0, 1] + gen_pwl(stim_file, "CSb", clk_times, values, period, slew, 0.05) - for key_time in key_times[:2]: - t_current = key_time - 0.25 * feasible_period - x_list.append(t_current) - y_list.append(vdd) - x_list.append(t_current + t_fall) - y_list.append(gnd) - - t_current = key_time + 0.25 * feasible_period - x_list.append(t_current) - y_list.append(gnd) - x_list.append(t_current + t_rise) - y_list.append(vdd) - - for key_time in key_times[2:-1]: - t_current = key_time - 0.25 * target_period - x_list.append(t_current) - y_list.append(vdd) - x_list.append(t_current + t_fall) - y_list.append(gnd) - - t_current = key_time + 0.25 * target_period - x_list.append(t_current) - y_list.append(gnd) - x_list.append(t_current + t_rise) - y_list.append(vdd) - - return (x_list, y_list) +def gen_web(stim_file, clk_times, period, slew): + """ Generates the PWL WEb signal""" + # values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP + values = [1, 0, 0, 0, 1, 0, 0, 1, 1] + gen_pwl(stim_file, "WEb", clk_times, values, period, slew, 0.05) + + values = [1, 0, 0, 0, 1, 0, 0, 1, 1] + gen_pwl(stim_file, "acc_en", clk_times, values, period, slew, 0) + values = [0, 1, 1, 1, 0, 1, 1, 0, 0] + gen_pwl(stim_file, "acc_en_inv", clk_times, values, period, slew, 0) + +def gen_oeb(stim_file, clk_times, period, slew): + """ Generates the PWL WEb signal""" + # values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP + values = [1, 1, 1, 1, 0, 1, 1, 0, 1] + gen_pwl(stim_file, "OEb", clk_times, values, period, slew, 0.05) -def gen_web_pwl(key_times, feasible_period, target_period, t_rise, t_fall): - """Returns two lists for x,y coordinates for the generation of WEb pwl""" - - t_current = 0.0 - x_list = [t_current] - y_list = [vdd] - - t_current = key_times[0] - 0.25 * feasible_period - x_list.append(t_current) - y_list.append(vdd) - x_list.append(t_current + t_fall) - y_list.append(gnd) - - t_current = key_times[0] + 0.25 * feasible_period - x_list.append(t_current) - y_list.append(gnd) - x_list.append(t_current + t_rise) - y_list.append(vdd) - - t_current = key_times[2] - 0.25 * target_period - x_list.append(t_current) - y_list.append(vdd) - x_list.append(t_current + t_fall) - y_list.append(gnd) - - t_current = key_times[2] + 0.25 * target_period - x_list.append(t_current) - y_list.append(gnd) - x_list.append(t_current + t_rise) - y_list.append(vdd) - - t_current = key_times[3] - 0.25 * target_period - x_list.append(t_current) - y_list.append(vdd) - x_list.append(t_current + t_fall) - y_list.append(gnd) - - t_current = key_times[3] + 0.25 * target_period - x_list.append(t_current) - y_list.append(gnd) - x_list.append(t_current + t_rise) - y_list.append(vdd) - - return (x_list, y_list) -def gen_oeb_pwl(key_times, feasible_period, target_period, t_rise, t_fall): - """Returns two lists for x,y coordinates for the generation of OEb pwl""" - - t_current = 0.0 - x_list = [t_current] - y_list = [vdd] - - t_current = key_times[1] - 0.25 * feasible_period - x_list.append(t_current) - y_list.append(vdd) - x_list.append(t_current + t_fall) - y_list.append(gnd) - - t_current = key_times[1] + 0.25 * feasible_period - x_list.append(t_current) - y_list.append(gnd) - x_list.append(t_current + t_rise) - y_list.append(vdd) - - t_current = key_times[4] - 0.25 * target_period - x_list.append(t_current) - y_list.append(vdd) - x_list.append(t_current + t_fall) - y_list.append(gnd) - - t_current = key_times[4] + 0.25 * target_period - x_list.append(t_current) - y_list.append(gnd) - x_list.append(t_current + t_rise) - y_list.append(vdd) - - return (x_list, y_list) - - -def gen_web_trans_pwl(key_times, feasible_period, target_period, t_rise, t_fall): - """Returns two lists for x,y coordinates for the generation of WEb_transmission_gate pwl""" - - t_current = 0.0 - x_list = [t_current] - y_list = [vdd] - - t_current = key_times[0] + 0.5 * feasible_period - t_current -= 0.25 * feasible_period - x_list.append(t_current) - y_list.append(vdd) - x_list.append(t_current + t_fall) - y_list.append(gnd) - - t_current += 0.5 * feasible_period - x_list.append(t_current) - y_list.append(gnd) - x_list.append(t_current + t_rise) - y_list.append(vdd) - - t_current = key_times[2] + 0.5 * target_period - t_current -= 0.25 * target_period - x_list.append(t_current) - y_list.append(vdd) - x_list.append(t_current + t_fall) - y_list.append(gnd) - - t_current += 0.5 * target_period - x_list.append(t_current) - y_list.append(gnd) - x_list.append(t_current + t_rise) - y_list.append(vdd) - - t_current = key_times[3] + 0.5 * target_period - t_current -= 0.25 * target_period - x_list.append(t_current) - y_list.append(vdd) - x_list.append(t_current + t_fall) - y_list.append(gnd) - - t_current += 0.5 * target_period - x_list.append(t_current) - y_list.append(gnd) - x_list.append(t_current + t_rise) - y_list.append(vdd) - - return (x_list, y_list) - - -def get_inverse_value(value): - if value > 0.5*vdd: - return gnd - elif value <= 0.5*vdd: - return vdd +def get_inverse_voltage(value): + if value > 0.5*vdd_voltage: + return gnd_voltage + elif value <= 0.5*vdd_voltage: + return vdd_voltage else: debug.error("Invalid value to get an inverse of: {0}".format(value)) - -def gen_pwl(stim_file, sig_name, x_list, y_list): - """Generates an arbitrary pwl for a signal where xlist is times in - ns and ylist is voltage. """ - - t_current = 0.0 - stim_file.write("V{0} {0} 0 PWL (".format(sig_name)) - for p in zip(x_list,y_list): - stim_file.write("{0}n {1}v ".format(p[0],p[1])) - stim_file.write(")\n") - -def gen_trap_pwl(stim_file, sig_name, x_list, y_list, t_rise, t_fall): - """Generates a trapezoidal pwl for a signal where xlist is times in ns and ylist is voltage. - Transitions are assumed to ignore slew and the slew rates are generated automatically - using the provided 0-100% slew times and centering times at the 50% point..""" - - stim_file.write("V{0} {0} 0 PWL (".format(sig_name)) - for p in zip(x_list,y_list): - slew = t_rise if p[1]>0.5*vdd else t_fall - start = max(p[0]-0.5*slew,0) - end = p[0]+0.5*slew - stim_file.write("{0}n {1}v ".format(start, get_inverse_value(p[1]))) - stim_file.write("{0}n {1}v ".format(end, p[1])) - stim_file.write(")\n") - - +def get_inverse_value(value): + if value > 0.5: + return 0 + elif value <= 0.5: + return 1 + else: + debug.error("Invalid value to get an inverse of: {0}".format(value)) + def gen_meas_delay(stim_file, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, td): """Creates the .meas statement for the measurement of delay""" - measure_string=".meas tran {0} TRIG v({1}) VAL={2} RISE={3} TARG v({4}) VAL={5} TD={7}n {6}=1\n" + measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={7}n TARG v({4}) VAL={5} {6}=1 TD={7}n\n\n" stim_file.write(measure_string.format(meas_name, trig_name, trig_val, @@ -411,7 +231,7 @@ def gen_meas_delay(stim_file, meas_name, trig_name, targ_name, trig_val, targ_va targ_val, targ_dir, td)) - + def gen_meas_power(stim_file, meas_name, t_initial, t_final): """Creates the .meas statement for the measurement of avg power""" # power mea cmd is different in different spice: @@ -419,27 +239,38 @@ def gen_meas_power(stim_file, meas_name, t_initial, t_final): power_exp = "power" else: power_exp = "par('(-1*v(" + str(vdd_name) + ")*I(v" + str(vdd_name) + "))')" - stim_file.write(".meas tran {0} avg {1} from={2}n to={3}n\n".format(meas_name, + stim_file.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name, power_exp, t_initial, t_final)) + stim_file.write("\n") + +def write_control(stim_file, end_time): + # UIC is needed for ngspice to converge + stim_file.write(".TRAN 5p {0}n UIC\n".format(end_time)) + stim_file.write(".OPTIONS POST=1 RUNLVL=4 PROBE\n") + # create plots for all signals + stim_file.write("* probe is used for hspice\n") + stim_file.write("*.probe V(*)\n") + stim_file.write("* plot is used for ngspice interactive mode \n") + stim_file.write("*.plot V(*)\n") + # end the stimulus file + stim_file.write(".end\n\n") def write_include(stim_file, models): """Writes include statements, inputs are lists of model files""" for item in list(models): - stim_file.write(".include \"{0}\"\n".format(item)) + stim_file.write(".include \"{0}\"\n\n".format(item)) -def write_supply(stim_file, vdd_name, gnd_name, vdd_voltage, gnd_voltage): +def write_supply(stim_file): """Writes supply voltage statements""" stim_file.write("V{0} {0} 0.0 {1}\n".format(vdd_name, vdd_voltage)) stim_file.write("V{0} {0} 0.0 {1}\n".format(gnd_name, gnd_voltage)) # This is for the test power supply stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+vdd_name, vdd_voltage)) - stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+gnd_name, gnd_voltage)) - - + stim_file.write("V{0} {0} 0.0 {1}\n\n".format("test"+gnd_name, gnd_voltage)) def run_sim(): diff --git a/compiler/debug.py b/compiler/debug.py index 63f7eaa3..5ab46d0b 100644 --- a/compiler/debug.py +++ b/compiler/debug.py @@ -34,6 +34,6 @@ def info(lev, str): if (OPTS.debug_level >= lev): frm = inspect.stack()[1] mod = inspect.getmodule(frm[0]) - print("\n[{0}]: {1}".format(frm[0].f_code.co_name,str)) + print("[{0}]: {1}".format(frm[0].f_code.co_name,str)) # This sometimes gets a NoneType mod... # print "[" , mod.__name__ , "]: ", str diff --git a/compiler/globals.py b/compiler/globals.py index 82304196..b656de2a 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -141,6 +141,17 @@ def read_config(config_file): if not OPTS.output_path.endswith('/'): OPTS.output_path += "/" debug.info(1, "Output saved in " + OPTS.output_path) + + # Don't delete the output dir, it may have other files! + # make the directory if it doesn't exist + try: + os.makedirs(OPTS.output_path, 0o750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(OPTS.output_path, 0o750) + except: + debug.error("Unable to make output directory.",-1) + def set_calibre(): @@ -219,13 +230,7 @@ def setup_paths(): if e.errno == 17: # errno.EEXIST os.chmod(OPTS.openram_temp, 0o750) - # Don't delete the output dir, it may have other files! - # make the directory if it doesn't exist - try: - os.makedirs(OPTS.output_path, 0o750) - except OSError as e: - if e.errno == 17: # errno.EEXIST - os.chmod(OPTS.output_path, 0o750) + diff --git a/compiler/hierarchical_decoder.py b/compiler/hierarchical_decoder.py index 491b0083..bfd03411 100644 --- a/compiler/hierarchical_decoder.py +++ b/compiler/hierarchical_decoder.py @@ -597,7 +597,7 @@ class hierarchical_decoder(design.design): yoffset - self.via_shift], rotate=90) - def delay(self, slope, load = 0.0): + def delay(self, slew, load = 0.0): # A -> out if self.determine_predecodes(self.num_inputs)[1]==0: pre = self.pre2_4 @@ -605,15 +605,15 @@ class hierarchical_decoder(design.design): else: pre = self.pre3_8 nand = self.nand3 - a_t_out_delay = pre.delay(slope=slope,load = nand.input_load()) + a_t_out_delay = pre.delay(slew=slew,load = nand.input_load()) # out -> z - out_t_z_delay = nand.delay(slope= a_t_out_delay.slope, + out_t_z_delay = nand.delay(slew= a_t_out_delay.slew, load = self.inv.input_load()) result = a_t_out_delay + out_t_z_delay # Z -> decode_out - z_t_decodeout_delay = self.inv.delay(slope = out_t_z_delay.slope , load = load) + z_t_decodeout_delay = self.inv.delay(slew = out_t_z_delay.slew , load = load) result = result + z_t_decodeout_delay return result diff --git a/compiler/hierarchical_predecode2x4.py b/compiler/hierarchical_predecode2x4.py index 2fc22097..c8b871c3 100644 --- a/compiler/hierarchical_predecode2x4.py +++ b/compiler/hierarchical_predecode2x4.py @@ -108,16 +108,16 @@ class hierarchical_predecode2x4(hierarchical_predecode): def get_via_y(self): return self.rail_height - def delay(self, slope, load = 0.0 ): + def delay(self, slew, load = 0.0 ): # A -> B - a_t_b_delay = self.inv.delay(slope=slope,load = self.nand.input_load()) + a_t_b_delay = self.inv.delay(slew=slew,load = self.nand.input_load()) # out -> z - b_t_z_delay = self.nand.delay(slope=a_t_b_delay.slope,load = self.inv.input_load()) + b_t_z_delay = self.nand.delay(slew=a_t_b_delay.slew,load = self.inv.input_load()) result = a_t_b_delay + b_t_z_delay # Z -> out - a_t_out_delay = self.inv.delay(slope=b_t_z_delay.slope,load = load) + a_t_out_delay = self.inv.delay(slew=b_t_z_delay.slew,load = load) result = result + a_t_out_delay return result diff --git a/compiler/hierarchical_predecode3x8.py b/compiler/hierarchical_predecode3x8.py index 9cefabeb..681ea602 100644 --- a/compiler/hierarchical_predecode3x8.py +++ b/compiler/hierarchical_predecode3x8.py @@ -90,13 +90,13 @@ class hierarchical_predecode3x8(hierarchical_predecode): - 0.5 * drc["minwidth_metal1"]) return yoffset - def delay(self, slope, load = 0.0 ): + def delay(self, slew, load = 0.0 ): # A -> z - b_t_z_delay = self.nand.delay(slope=slope, + b_t_z_delay = self.nand.delay(slew=slew, load = self.input_load()) # Z -> out - a_t_out_delay = self.inv.delay(slope=b_t_z_delay.slope, + a_t_out_delay = self.inv.delay(slew=b_t_z_delay.slew, load = load) result = b_t_z_delay + a_t_out_delay return result diff --git a/compiler/hierarchy_spice.py b/compiler/hierarchy_spice.py index f818626c..bf807bba 100644 --- a/compiler/hierarchy_spice.py +++ b/compiler/hierarchy_spice.py @@ -148,7 +148,7 @@ class spice: del usedMODS spfile.close() - def delay(self, slope, load=0.0): + def delay(self, slew, load=0.0): """Inform users undefined delay module while building new modules""" debug.warning("Design Class {0} delay function needs to be defined" .format(self.__class__.__name__)) @@ -158,7 +158,7 @@ class spice: # return 0 to keep code running while building return delay_data(0.0, 0.0) - def cal_delay_with_rc(self, r, c ,slope, swing = 0.5): + def cal_delay_with_rc(self, r, c ,slew, swing = 0.5): """ Calculate the delay of a mosfet by modeling it as a resistance driving a capacitance @@ -167,19 +167,19 @@ class spice: delay = swing_factor * r * c #c is in ff and delay is in fs delay = delay * 0.001 #make the unit to ps - # Output slope should be linear to input slope which is described - # as 0.005* slope. + # Output slew should be linear to input slew which is described + # as 0.005* slew. - # The slope will be also influenced by the delay. - # If no input slope(or too small to make impact) - # The mimum slope should be the time to charge RC. + # The slew will be also influenced by the delay. + # If no input slew(or too small to make impact) + # The mimum slew should be the time to charge RC. # Delay * 2 is from 0 to 100% swing. 0.6*2*delay is from 20%-80%. - slope = delay * 0.6 * 2 + 0.005 * slope - return delay_data(delay = delay, slope = slope) + slew = delay * 0.6 * 2 + 0.005 * slew + return delay_data(delay = delay, slew = slew) - def return_delay(self, delay, slope): - return delay_data(delay, slope) + def return_delay(self, delay, slew): + return delay_data(delay, slew) def generate_rc_net(self,lump_num, wire_length, wire_width): return wire_spice_model(lump_num, wire_length, wire_width) @@ -188,17 +188,17 @@ class delay_data: """ This is the delay class to represent the delay information Time is 50% of the signal to 50% of reference signal delay. - Slope is the 20% of the signal to 80% of signal + Slew is the 10% of the signal to 90% of signal """ - def __init__(self, delay=0.0, slope=0.0): + def __init__(self, delay=0.0, slew=0.0): """ init function support two init method""" # will take single input as a coordinate self.delay = delay - self.slope = slope + self.slew = slew def __str__(self): """ override print function output """ - return "Delay Data: Delay "+str(self.delay)+", Slope "+str(self.slope)+"" + return "Delay Data: Delay "+str(self.delay)+", Slew "+str(self.slew)+"" def __add__(self, other): """ @@ -206,7 +206,7 @@ class delay_data: """ assert isinstance(other,delay_data) return delay_data(other.delay + self.delay, - other.slope) + other.slew) def __radd__(self, other): """ @@ -214,7 +214,7 @@ class delay_data: """ assert isinstance(other,delay_data) return delay_data(other.delay + self.delay, - self.slope) + self.slew) class wire_spice_model: @@ -241,14 +241,14 @@ class wire_spice_model: def return_input_cap(self): return 0.5 * self.wire_c * self.lump_num - def return_delay_over_wire(self, slope, swing = 0.5): + def return_delay_over_wire(self, slew, swing = 0.5): # delay will be sum of arithmetic sequence start from # rc to self.lump_num*rc with step of rc swing_factor = abs(math.log(1-swing)) # time constant based on swing sum_factor = (1+self.lump_num) * self.lump_num * 0.5 # sum of the arithmetic sequence delay = sum_factor * swing_factor * self.wire_r * self.wire_c - slope = delay * 2 + slope - result= delay_data(delay, slope) + slew = delay * 2 + slew + result= delay_data(delay, slew) return result diff --git a/compiler/ms_flop.py b/compiler/ms_flop.py index d1342817..035cd00d 100644 --- a/compiler/ms_flop.py +++ b/compiler/ms_flop.py @@ -24,19 +24,19 @@ class ms_flop(design.design): self.dout_offset = ms_flop.chars["dout"] self.dout_bar_offset = ms_flop.chars["dout_bar"] - def delay(self, slope, load = 0.0): + def delay(self, slew, load = 0.0): #import pinv # use inv to mimic the delay # din -> mout #ref = pinv.pinv("reference_inv") #mid_load = ref.input_load() - #din_t_mout_delay = ref.delay(slope = slope, load = mid_load) + #din_t_mout_delay = ref.delay(slew = slew, load = mid_load) # mout -> out - #mout_t_out_delay = ref.delay(slope = slope, load = load) + #mout_t_out_delay = ref.delay(slew = slew, load = load) #result = din_t_mout_delay + mout_t_out_delay # dont k how to calculate this now, use constant in tech file from tech import spice - result = self.return_delay(spice["msflop_delay"], spice["msflop_slope"]) + result = self.return_delay(spice["msflop_delay"], spice["msflop_slew"]) return result diff --git a/compiler/ms_flop_array.py b/compiler/ms_flop_array.py index 79a52eab..8599f90e 100644 --- a/compiler/ms_flop_array.py +++ b/compiler/ms_flop_array.py @@ -150,7 +150,7 @@ class ms_flop_array(design.design): self.vdd_positions.append(vector(self.ms_flop_chars["vdd"]).scale(0, 1)) - def delay(self, slope, load=0.0): - result = self.ms_flop.delay(slope = slope, + def delay(self, slew, load=0.0): + result = self.ms_flop.delay(slew = slew, load = load) return result diff --git a/compiler/nand_2.py b/compiler/nand_2.py index 26202e8d..2c742962 100644 --- a/compiler/nand_2.py +++ b/compiler/nand_2.py @@ -26,7 +26,7 @@ class nand_2(design.design): name = "nand2_{0}".format(nand_2.unique_id) nand_2.unique_id += 1 design.design.__init__(self, name) - debug.info(2, "create nand_2 strcuture {0} with size of {1}".format( + debug.info(2, "create nand_2 structure {0} with size of {1}".format( name, nmos_width)) self.nmos_width = nmos_width @@ -447,7 +447,7 @@ class nand_2(design.design): from tech import spice return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] - def delay(self, slope, load=0.0): + def delay(self, slew, load=0.0): r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) - c_para = spice["min_tx_c_para"]*(self.nmos_size/parameter["min_tx_size"])#ff - return self.cal_delay_with_rc(r = r, c = c_para+load, slope =slope) + c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff + return self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew) diff --git a/compiler/nand_3.py b/compiler/nand_3.py index 44c4c41e..a570d3f7 100644 --- a/compiler/nand_3.py +++ b/compiler/nand_3.py @@ -26,7 +26,7 @@ class nand_3(design.design): name = "nand3_{0}".format(nand_3.unique_id) nand_3.unique_id += 1 design.design.__init__(self, name) - debug.info(2, "create nand_3 strcuture {0} with size of {1}".format(name, nmos_width)) + debug.info(2, "create nand_3 structure {0} with size of {1}".format(name, nmos_width)) self.nmos_width = nmos_width self.height = height @@ -532,7 +532,7 @@ class nand_3(design.design): def input_load(self): return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] - def delay(self, slope, load=0.0): + def delay(self, slew, load=0.0): r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) - c_para = spice["min_tx_c_para"]*(self.nmos_size/parameter["min_tx_size"])#ff - return self.cal_delay_with_rc(r = r, c = c_para+load, slope =slope) + c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff + return self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew) diff --git a/compiler/nor_2.py b/compiler/nor_2.py index 6bc69f1e..454cd9c4 100644 --- a/compiler/nor_2.py +++ b/compiler/nor_2.py @@ -25,7 +25,7 @@ class nor_2(design.design): name = "nor2_{0}".format(nor_2.unique_id) nor_2.unique_id += 1 design.design.__init__(self, name) - debug.info(2, "create nand_2 strcuture {0} with size of {1}".format(name, nmos_width)) + debug.info(2, "create nor_2 structure {0} with size of {1}".format(name, nmos_width)) self.nmos_width = nmos_width self.height = height diff --git a/compiler/pinv.py b/compiler/pinv.py index 526fee6d..2485549c 100644 --- a/compiler/pinv.py +++ b/compiler/pinv.py @@ -23,7 +23,7 @@ class pinv(design.design): name = "pinv{0}".format(pinv.unique_id) pinv.unique_id += 1 design.design.__init__(self, name) - debug.info(2, "create pinv strcuture {0} with size of {1}".format(name, nmos_width)) + debug.info(2, "create pinv structure {0} with size of {1}".format(name, nmos_width)) self.nmos_width = nmos_width self.beta = beta @@ -410,8 +410,8 @@ class pinv(design.design): def input_load(self): return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] - def delay(self, slope, load=0.0): + def delay(self, slew, load=0.0): from tech import spice r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) - c_para = spice["min_tx_c_para"]*(self.nmos_size/parameter["min_tx_size"])#ff - return self.cal_delay_with_rc(r = r, c = c_para+load, slope =slope) + c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff + return self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew) diff --git a/compiler/sense_amp.py b/compiler/sense_amp.py index 4c2be560..2fe0fce0 100644 --- a/compiler/sense_amp.py +++ b/compiler/sense_amp.py @@ -21,10 +21,10 @@ class sense_amp(design.design): self.width = sense_amp.chars["width"] self.height = sense_amp.chars["height"] - def delay(self, slope, load=0.0): + def delay(self, slew, load=0.0): from tech import spice r = spice["min_tx_r"]/(10) - c_para = spice["min_tx_c_para"]#ff - result = self.cal_delay_with_rc(r = r, c = c_para+load, slope =slope) - return self.return_delay(result.delay , result.slope) + c_para = spice["min_tx_drain_c"] + result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew) + return self.return_delay(result.delay, result.slew) diff --git a/compiler/sense_amp_array.py b/compiler/sense_amp_array.py index 6f5f4de2..cfbb3cb5 100644 --- a/compiler/sense_amp_array.py +++ b/compiler/sense_amp_array.py @@ -132,6 +132,6 @@ class sense_amp_array(design.design): height=drc["minwidth_metal1"]) self.SCLK_positions.append(sclk_offset) - def delay(self, slope, load =0.0): - result = self.amp.delay(slope=slope, load =load) + def delay(self, slew, load=0.0): + result = self.amp.delay(slew=slew, load=load) return result diff --git a/compiler/sram.py b/compiler/sram.py index d3c50f68..45e7ef80 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -11,7 +11,6 @@ import getpass from vector import vector from globals import OPTS - class sram(design.design): """ Dynamically generated SRAM by connecting banks to control logic. The @@ -1174,12 +1173,28 @@ class sram(design.design): del usedMODS sp.close() - def analytical_model(self,slope): - #control_delay = self.control.delay(slope=slope) - bank_delay = self.bank.delay(slope = slope) - data ={'delay1': bank_delay.delay, 'delay0': bank_delay.delay, - 'min_period1': 0, - 'min_period0': 0, - 'read_power': 0, - 'write_power': 0} + def analytical_model(self,slews,loads): + LH_delay = [] + HL_delay = [] + LH_slew = [] + HL_slew = [] + for slew in slews: + for load in loads: + bank_delay = self.bank.delay(slew,load) + # Convert from ps to ns + LH_delay.append(bank_delay.delay/1e3) + HL_delay.append(bank_delay.delay/1e3) + LH_slew.append(bank_delay.slew/1e3) + HL_slew.append(bank_delay.slew/1e3) + + data = {"min_period": 0, + "delay1": LH_delay, + "delay0": HL_delay, + "slew1": LH_slew, + "slew0": HL_slew, + "read0_power": 0, + "read1_power": 0, + "write0_power": 0, + "write1_power": 0 + } return data diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index c1354790..18a52f49 100644 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -46,21 +46,40 @@ class timing_sram_test(unittest.TestCase): debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) d = delay.delay(s,tempspice) - data = d.analyze(probe_address, probe_data) + import tech + loads = [tech.spice["FF_in_cap"]*4] + slews = [tech.spice["rise_time"]*2] + data = d.analyze(probe_address, probe_data,slews,loads) if OPTS.tech_name == "freepdk45": - self.assertTrue(isclose(data['delay1'],0.013649)) - self.assertTrue(isclose(data['delay0'],0.22893)) - self.assertTrue(isclose(data['min_period1'],0.078582763671875)) - self.assertTrue(isclose(data['min_period0'],0.25543212890625)) + self.assertTrue(isclose(data['delay1'][0],0.0262)) # diff than hspice + self.assertTrue(isclose(data['delay0'][0],0.1099)) # diff than hspice + self.assertTrue(isclose(data['slew1'][0],0.0210)) # diff than hspice + self.assertTrue(isclose(data['slew0'][0],0.0270)) # diff than hspice + self.assertTrue(isclose(data['min_period'],0.068)) # diff than hspice + self.assertTrue(isclose(data['read0_power'],0.01782)) # diff than hspice + self.assertTrue(isclose(data['read1_power'],0.01778)) # diff than hspice + self.assertTrue(isclose(data['write0_power'],0.01663)) # diff than hspice + self.assertTrue(isclose(data['write1_power'],0.01592)) # diff than hspice elif OPTS.tech_name == "scn3me_subm": - self.assertTrue(isclose(data['delay1'],1.5335)) - self.assertTrue(isclose(data['delay0'],2.2635000000000005)) - self.assertTrue(isclose(data['min_period1'],1.53564453125)) - self.assertTrue(isclose(data['min_period0'],2.998046875)) + self.assertTrue(isclose(data['delay1'][0],0.5985)) # diff than hspice + self.assertTrue(isclose(data['delay0'][0],1.3726)) # diff than hspice + self.assertTrue(isclose(data['slew1'][0],1.0046)) # diff than hspice + self.assertTrue(isclose(data['slew0'][0],1.3013)) # diff than hspice + self.assertTrue(isclose(data['min_period'],1.953)) # diff than hspice + self.assertTrue(isclose(data['read0_power'],4.5491)) # diff than hspice + self.assertTrue(isclose(data['read1_power'],4.5202)) # diff than hspice + self.assertTrue(isclose(data['write0_power'],3.8564)) # diff than hspice + self.assertTrue(isclose(data['write1_power'],3.7287)) # diff than hspice else: self.assertTrue(False) # other techs fail + # reset these options + OPTS.check_lvsdrc = True + OPTS.spice_version="hspice" + OPTS.force_spice = False + globals.set_spice() + os.remove(tempspice) globals.end_openram() diff --git a/compiler/tests/21_hspice_hold_test.py b/compiler/tests/21_hspice_hold_test.py deleted file mode 100644 index 533a0afd..00000000 --- a/compiler/tests/21_hspice_hold_test.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python2.7 -""" -Run a regresion test on various srams -""" - -import unittest -from testutils import header,isclose -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -import debug -import calibre - -OPTS = globals.get_opts() - -#@unittest.skip("SKIPPING 21_timing_sram_test") - - -class timing_setup_test(unittest.TestCase): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - # we will manually run lvs/drc - OPTS.check_lvsdrc = False - OPTS.spice_version="hspice" - OPTS.force_spice = True - globals.set_spice() - - import sram - import setup_hold - - sh = setup_hold.setup_hold() - [one_setup_time, zero_setup_time] = sh.hold_time() - - OPTS.check_lvsdrc = True - - if OPTS.tech_name == "freepdk45": - self.assertTrue(isclose(one_setup_time,-0.0048828125)) - self.assertTrue(isclose(zero_setup_time,-0.010986328125)) - elif OPTS.tech_name == "scn3me_subm": - self.assertTrue(isclose(one_setup_time,0.04638671875)) - self.assertTrue(isclose(zero_setup_time,-0.0830078125)) - else: - self.assertTrue(False) # other techs fail - - globals.end_openram() - -# instantiate a copdsay of the class to actually run the test -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main() diff --git a/compiler/tests/21_hspice_setup_test.py b/compiler/tests/21_hspice_setuphold_test.py similarity index 58% rename from compiler/tests/21_hspice_setup_test.py rename to compiler/tests/21_hspice_setuphold_test.py index 6e264531..1e60b11c 100644 --- a/compiler/tests/21_hspice_setup_test.py +++ b/compiler/tests/21_hspice_setuphold_test.py @@ -28,18 +28,31 @@ class timing_setup_test(unittest.TestCase): globals.set_spice() import sram + import tech + slews = [tech.spice["rise_time"]*2] + import setup_hold sh = setup_hold.setup_hold() - [one_setup_time, zero_setup_time] = sh.setup_time() + data = sh.analyze(slews,slews) OPTS.check_lvsdrc = True + + one_setup_time = data['setup_times_LH'][0] + zero_setup_time = data['setup_times_HL'][0] + one_hold_time = data['hold_times_LH'][0] + zero_hold_time = data['hold_times_HL'][0] + if OPTS.tech_name == "freepdk45": - self.assertTrue(isclose(one_setup_time,0.0146484375)) - self.assertTrue(isclose(zero_setup_time,0.008544921875)) + self.assertTrue(isclose(one_setup_time,0.0146)) + self.assertTrue(isclose(zero_setup_time,0.0085)) + self.assertTrue(isclose(one_hold_time,0.00244)) + self.assertTrue(isclose(zero_hold_time,-0.00366)) elif OPTS.tech_name == "scn3me_subm": - self.assertTrue(isclose(one_setup_time,0.0927734375)) - self.assertTrue(isclose(zero_setup_time,-0.0244140625)) + self.assertTrue(isclose(one_setup_time,0.1001)) + self.assertTrue(isclose(zero_setup_time,0.02075)) + self.assertTrue(isclose(one_hold_time,0.02075)) + self.assertTrue(isclose(zero_hold_time,-0.0830)) else: self.assertTrue(False) # other techs fail diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 50985119..f225ec97 100644 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -32,12 +32,6 @@ class timing_sram_test(unittest.TestCase): num_banks=OPTS.config.num_banks, name="test_sram1") - # reset these options - OPTS.check_lvsdrc = True - OPTS.spice_version="hspice" - OPTS.force_spice = False - globals.set_spice() - import delay tempspice = OPTS.openram_temp + "temp.sp" @@ -48,23 +42,44 @@ class timing_sram_test(unittest.TestCase): debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) d = delay.delay(s,tempspice) - data = d.analyze(probe_address, probe_data) + import tech + loads = [tech.spice["FF_in_cap"]*4] + slews = [tech.spice["rise_time"]*2] + data = d.analyze(probe_address, probe_data,slews,loads) if OPTS.tech_name == "freepdk45": - self.assertTrue(isclose(data['delay1'],0.013649)) # diff than hspice - self.assertTrue(isclose(data['delay0'],0.22893)) # diff than hspice - self.assertTrue(isclose(data['min_period1'],0.078582763671875)) # diff than hspice - self.assertTrue(isclose(data['min_period0'],0.25543212890625)) # diff than hspice + self.assertTrue(isclose(data['delay1'][0],0.0268)) # diff than hspice + self.assertTrue(isclose(data['delay0'][0],0.1127)) # diff than hspice + self.assertTrue(isclose(data['slew1'][0],0.0231)) # diff than hspice + self.assertTrue(isclose(data['slew0'][0],0.0276)) # diff than hspice + self.assertTrue(isclose(data['min_period'],0.071)) # diff than hspice + self.assertTrue(isclose(data['read0_power'],0.0227)) # diff than hspice + self.assertTrue(isclose(data['read1_power'],0.0223)) # diff than hspice + self.assertTrue(isclose(data['write0_power'],0.02001)) # diff than hspice + self.assertTrue(isclose(data['write1_power'],0.0193)) # diff than hspice elif OPTS.tech_name == "scn3me_subm": - self.assertTrue(isclose(data['delay1'],1.5342000000000002)) # diff than hspice - self.assertTrue(isclose(data['delay0'],2.2698)) # diff than hspice - self.assertTrue(isclose(data['min_period1'],1.534423828125)) # diff than hspice - self.assertTrue(isclose(data['min_period0'],2.99560546875)) # diff than hspice + self.assertTrue(isclose(data['delay1'][0],0.6228)) # diff than hspice + self.assertTrue(isclose(data['delay0'][0],1.4147)) # diff than hspice + self.assertTrue(isclose(data['slew1'][0],1.0567)) # diff than hspice + self.assertTrue(isclose(data['slew0'][0],1.3454)) # diff than hspice + self.assertTrue(isclose(data['min_period'],1.719)) # diff than hspice + self.assertTrue(isclose(data['read0_power'],4.7812)) # diff than hspice + self.assertTrue(isclose(data['read1_power'],5.5500)) # diff than hspice + self.assertTrue(isclose(data['write0_power'],3.9314)) # diff than hspice + self.assertTrue(isclose(data['write1_power'],3.4097)) # diff than hspice else: self.assertTrue(False) # other techs fail + # reset these options + OPTS.check_lvsdrc = True + OPTS.spice_version="hspice" + OPTS.force_spice = False + globals.set_spice() + os.remove(tempspice) + globals.end_openram() + # instantiate a copdsay of the class to actually run the test if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/21_ngspice_setup_test.py b/compiler/tests/21_ngspice_setup_test.py deleted file mode 100644 index 79d1b218..00000000 --- a/compiler/tests/21_ngspice_setup_test.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python2.7 -""" -Run a regresion test on various srams -""" - -import unittest -from testutils import header,isclose -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -import debug -import calibre - -OPTS = globals.get_opts() - -#@unittest.skip("SKIPPING 21_timing_sram_test") - - -class timing_setup_test(unittest.TestCase): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - # we will manually run lvs/drc - OPTS.check_lvsdrc = False - OPTS.spice_version="ngspice" - OPTS.force_spice = True - globals.set_spice() - - import sram - import setup_hold - - sh = setup_hold.setup_hold() - [one_setup_time, zero_setup_time] = sh.setup_time() - - # reset these options - OPTS.check_lvsdrc = True - OPTS.spice_version="hspice" - OPTS.force_spice = False - globals.set_spice() - - if OPTS.tech_name == "freepdk45": - self.assertTrue(isclose(one_setup_time,0.0146484375)) - self.assertTrue(isclose(zero_setup_time,0.008544921875)) - elif OPTS.tech_name == "scn3me_subm": - self.assertTrue(isclose(one_setup_time,0.09521484375)) #diff than hspice - self.assertTrue(isclose(zero_setup_time,-0.0244140625)) - else: - self.assertTrue(False) # other techs fail - -# instantiate a copdsay of the class to actually run the test -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main() diff --git a/compiler/tests/21_ngspice_hold_test.py b/compiler/tests/21_ngspice_setuphold_test.py similarity index 60% rename from compiler/tests/21_ngspice_hold_test.py rename to compiler/tests/21_ngspice_setuphold_test.py index f8c891ee..329057f0 100644 --- a/compiler/tests/21_ngspice_hold_test.py +++ b/compiler/tests/21_ngspice_setuphold_test.py @@ -27,10 +27,13 @@ class timing_setup_test(unittest.TestCase): globals.set_spice() import sram + import tech + slews = [tech.spice["rise_time"]*2] + import setup_hold sh = setup_hold.setup_hold() - [one_setup_time, zero_setup_time] = sh.hold_time() + data = sh.analyze(slews,slews) # reset these options OPTS.check_lvsdrc = True @@ -38,12 +41,21 @@ class timing_setup_test(unittest.TestCase): OPTS.force_spice = False globals.set_spice() + one_setup_time = data['setup_times_LH'][0] + zero_setup_time = data['setup_times_HL'][0] + one_hold_time = data['hold_times_LH'][0] + zero_hold_time = data['hold_times_HL'][0] + if OPTS.tech_name == "freepdk45": - self.assertTrue(isclose(one_setup_time,-0.0048828125)) - self.assertTrue(isclose(zero_setup_time,-0.010986328125)) + self.assertTrue(isclose(one_setup_time,0.0146)) + self.assertTrue(isclose(zero_setup_time,0.0085)) + self.assertTrue(isclose(one_hold_time,0.00244)) + self.assertTrue(isclose(zero_hold_time,-0.00366)) elif OPTS.tech_name == "scn3me_subm": - self.assertTrue(isclose(one_setup_time,0.45654296875)) # diff than hspice - self.assertTrue(isclose(zero_setup_time,-0.0830078125)) + self.assertTrue(isclose(one_setup_time,0.1001)) + self.assertTrue(isclose(zero_setup_time,0.0208)) + self.assertTrue(isclose(one_hold_time,0.02075)) + self.assertTrue(isclose(zero_hold_time,-0.08301)) else: self.assertTrue(False) # other techs fail diff --git a/compiler/tests/22_sram_func_test.py b/compiler/tests/22_sram_func_test.py index e197d49b..45fc7160 100644 --- a/compiler/tests/22_sram_func_test.py +++ b/compiler/tests/22_sram_func_test.py @@ -46,7 +46,10 @@ class sram_func_test(unittest.TestCase): d.set_probe(probe_address,probe_data) # This will exit if it doesn't find a feasible period - feasible_period = d.find_feasible_period(2.0) + import tech + load = tech.spice["FF_in_cap"]*4 + slew = tech.spice["rise_time"]*2 + feasible_period = d.find_feasible_period(load,slew) os.remove(tempspice) diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45.lib b/compiler/tests/golden/sram_2_16_1_freepdk45.lib index 70723d79..358de25a 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45.lib @@ -31,52 +31,18 @@ library (sram_2_16_1_freepdk45_lib){ default_max_fanout : 4.0 ; default_connection_class : universal ; - lu_table_template(CELL_UP_FOR_CLOCK){ + lu_table_template(CELL_TABLE){ variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; - index_1 ("0.5"); - index_2 ("0.5"); + index_1("0.00125, 0.005, 0.04"); + index_2("0.052275, 0.2091, 1.6728"); } - lu_table_template(CELL_DN_FOR_CLOCK){ - variable_1 : input_net_transition; - variable_2 : total_output_net_capacitance; - index_1 ("0.5"); - index_2 ("0.5"); - } - - lu_table_template(CONSTRAINT_HIGH_POS){ + lu_table_template(CONSTRAINT_TABLE){ variable_1 : related_pin_transition; variable_2 : constrained_pin_transition; - index_1 ("0.5"); - index_2 ("0.5"); - } - - lu_table_template(CONSTRAINT_LOW_POS){ - variable_1 : related_pin_transition; - variable_2 : constrained_pin_transition; - index_1 ("0.5"); - index_2 ("0.5"); - } - - lu_table_template(CLK_TRAN) { - variable_1 : constrained_pin_transition; - index_1 ("0.5"); - } - - lu_table_template(TRAN) { - variable_1 : total_output_net_capacitance; - index_1 ("0.5"); - } - - power_lut_template(INPUT_BY_TRANS_FOR_CLOCK){ - variable_1 : input_transition_time; - index_1 ("0.5"); - } - - power_lut_template(INPUT_BY_TRANS_FOR_SIGNAL){ - variable_1 : input_transition_time; - index_1 ("0.5"); + index_1("0.00125, 0.005, 0.04"); + index_2("0.00125, 0.005, 0.04"); } default_operating_conditions : TT; @@ -113,102 +79,126 @@ cell (sram_2_16_1_freepdk45){ bus(DATA){ bus_type : DATA; direction : inout; - max_capacitance : 0.62166; - pin(DATA[1:0]){ - } + max_capacitance : 1.6728; three_state : "!OEb & !clk"; memory_write(){ address : ADDR; clocked_on : clk; } + memory_read(){ + address : ADDR; + } + pin(DATA[1:0]){ internal_power(){ when : "OEb & !clk"; - rise_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("0.67729"); + rise_power(scalar){ + values("0.020845"); } - fall_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("0.67729"); + fall_power(scalar){ + values("0.021849"); } } timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.015"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.015, 0.027",\ + "0.009, 0.015, 0.027",\ + "0.009, 0.015, 0.027"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("0.009"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.015",\ + "0.009, 0.009, 0.015",\ + "0.009, 0.009, 0.015"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("-0.005"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.002, 0.002, -0.004",\ + "0.002, 0.002, -0.004",\ + "0.002, 0.002, -0.004"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.011"); + fall_constraint(CONSTRAINT_TABLE) { + values("-0.004, -0.004, -0.016",\ + "-0.004, -0.004, -0.016",\ + "-0.004, -0.004, -0.016"); } } - memory_read(){ - address : ADDR; - } internal_power(){ when : "!OEb & !clk"; - rise_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("0.028881"); + rise_power(scalar){ + values("0.023616"); } - fall_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("0.028881"); + fall_power(scalar){ + values("0.023883"); } } timing(){ timing_sense : non_unate; related_pin : "clk"; - timing_type : rising_edge; - cell_rise(CELL_UP_FOR_CLOCK) { - values("0.019"); + timing_type : falling_edge; + cell_rise(CELL_TABLE) { + values("0.025, 0.026, 0.032",\ + "0.026, 0.026, 0.033",\ + "0.03, 0.031, 0.037"); } - cell_fall(CELL_DN_FOR_CLOCK) { - values("0.239"); + cell_fall(CELL_TABLE) { + values("0.11, 0.11, 0.118",\ + "0.11, 0.111, 0.119",\ + "0.114, 0.114, 0.122"); } - rise_transition(TRAN) { - values("0.019"); + rise_transition(CELL_TABLE) { + values("0.016, 0.017, 0.028",\ + "0.016, 0.017, 0.028",\ + "0.016, 0.018, 0.028"); } - fall_transition(TRAN) { - values("0.239"); + fall_transition(CELL_TABLE) { + values("0.02, 0.022, 0.037",\ + "0.02, 0.022, 0.037",\ + "0.021, 0.023, 0.037"); } } + } } bus(ADDR){ bus_type : ADDR; direction : input; capacitance : 0.2091; - max_transition : 0.5; + max_transition : 0.04; fanout_load : 1.000000; pin(ADDR[3:0]){ - } timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.015"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.015, 0.027",\ + "0.009, 0.015, 0.027",\ + "0.009, 0.015, 0.027"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("0.009"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.015",\ + "0.009, 0.009, 0.015",\ + "0.009, 0.009, 0.015"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("-0.005"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.002, 0.002, -0.004",\ + "0.002, 0.002, -0.004",\ + "0.002, 0.002, -0.004"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.011"); + fall_constraint(CONSTRAINT_TABLE) { + values("-0.004, -0.004, -0.016",\ + "-0.004, -0.004, -0.016",\ + "-0.004, -0.004, -0.016"); } } + } } pin(CSb){ @@ -217,21 +207,29 @@ cell (sram_2_16_1_freepdk45){ timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.015"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.015, 0.027",\ + "0.009, 0.015, 0.027",\ + "0.009, 0.015, 0.027"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("0.009"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.015",\ + "0.009, 0.009, 0.015",\ + "0.009, 0.009, 0.015"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("-0.005"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.002, 0.002, -0.004",\ + "0.002, 0.002, -0.004",\ + "0.002, 0.002, -0.004"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.011"); + fall_constraint(CONSTRAINT_TABLE) { + values("-0.004, -0.004, -0.016",\ + "-0.004, -0.004, -0.016",\ + "-0.004, -0.004, -0.016"); } } } @@ -242,21 +240,29 @@ cell (sram_2_16_1_freepdk45){ timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.015"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.015, 0.027",\ + "0.009, 0.015, 0.027",\ + "0.009, 0.015, 0.027"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("0.009"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.015",\ + "0.009, 0.009, 0.015",\ + "0.009, 0.009, 0.015"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("-0.005"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.002, 0.002, -0.004",\ + "0.002, 0.002, -0.004",\ + "0.002, 0.002, -0.004"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.011"); + fall_constraint(CONSTRAINT_TABLE) { + values("-0.004, -0.004, -0.016",\ + "-0.004, -0.004, -0.016",\ + "-0.004, -0.004, -0.016"); } } } @@ -267,21 +273,29 @@ cell (sram_2_16_1_freepdk45){ timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.015"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.015, 0.027",\ + "0.009, 0.015, 0.027",\ + "0.009, 0.015, 0.027"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("0.009"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.015",\ + "0.009, 0.009, 0.015",\ + "0.009, 0.009, 0.015"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("-0.005"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.002, 0.002, -0.004",\ + "0.002, 0.002, -0.004",\ + "0.002, 0.002, -0.004"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.011"); + fall_constraint(CONSTRAINT_TABLE) { + values("-0.004, -0.004, -0.016",\ + "-0.004, -0.004, -0.016",\ + "-0.004, -0.004, -0.016"); } } } @@ -293,21 +307,21 @@ cell (sram_2_16_1_freepdk45){ timing(){ timing_type :"min_pulse_width"; related_pin : clk; - rise_constraint(CLK_TRAN) { - values("0.175"); + rise_constraint(scalar) { + values("0.0405"); } - fall_constraint(CLK_TRAN) { - values("0.175"); + fall_constraint(scalar) { + values("0.0405"); } } timing(){ timing_type :"minimum_period"; related_pin : clk; - rise_constraint(CLK_TRAN) { - values("0.35"); + rise_constraint(scalar) { + values("0.081"); } - fall_constraint(CLK_TRAN) { - values("0.35"); + fall_constraint(scalar) { + values("0.081"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_analytical.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_analytical.lib index 1f01dbf8..0a107645 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_analytical.lib @@ -31,52 +31,18 @@ library (sram_2_16_1_freepdk45_lib){ default_max_fanout : 4.0 ; default_connection_class : universal ; - lu_table_template(CELL_UP_FOR_CLOCK){ + lu_table_template(CELL_TABLE){ variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; - index_1 ("0.5"); - index_2 ("0.5"); + index_1("0.00125, 0.005, 0.04"); + index_2("0.052275, 0.2091, 1.6728"); } - lu_table_template(CELL_DN_FOR_CLOCK){ - variable_1 : input_net_transition; - variable_2 : total_output_net_capacitance; - index_1 ("0.5"); - index_2 ("0.5"); - } - - lu_table_template(CONSTRAINT_HIGH_POS){ + lu_table_template(CONSTRAINT_TABLE){ variable_1 : related_pin_transition; variable_2 : constrained_pin_transition; - index_1 ("0.5"); - index_2 ("0.5"); - } - - lu_table_template(CONSTRAINT_LOW_POS){ - variable_1 : related_pin_transition; - variable_2 : constrained_pin_transition; - index_1 ("0.5"); - index_2 ("0.5"); - } - - lu_table_template(CLK_TRAN) { - variable_1 : constrained_pin_transition; - index_1 ("0.5"); - } - - lu_table_template(TRAN) { - variable_1 : total_output_net_capacitance; - index_1 ("0.5"); - } - - power_lut_template(INPUT_BY_TRANS_FOR_CLOCK){ - variable_1 : input_transition_time; - index_1 ("0.5"); - } - - power_lut_template(INPUT_BY_TRANS_FOR_SIGNAL){ - variable_1 : input_transition_time; - index_1 ("0.5"); + index_1("0.00125, 0.005, 0.04"); + index_2("0.00125, 0.005, 0.04"); } default_operating_conditions : TT; @@ -113,102 +79,126 @@ cell (sram_2_16_1_freepdk45){ bus(DATA){ bus_type : DATA; direction : inout; - max_capacitance : 0.62166; - pin(DATA[1:0]){ - } + max_capacitance : 1.6728; three_state : "!OEb & !clk"; memory_write(){ address : ADDR; clocked_on : clk; } + memory_read(){ + address : ADDR; + } + pin(DATA[1:0]){ internal_power(){ when : "OEb & !clk"; - rise_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("0.0"); + rise_power(scalar){ + values("0"); } - fall_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("0.0"); + fall_power(scalar){ + values("0"); } } timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.015"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("0.009"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("-0.005"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.011"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } - memory_read(){ - address : ADDR; - } internal_power(){ when : "!OEb & !clk"; - rise_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("0.0"); + rise_power(scalar){ + values("0"); } - fall_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("0.0"); + fall_power(scalar){ + values("0"); } } timing(){ timing_sense : non_unate; related_pin : "clk"; - timing_type : rising_edge; - cell_rise(CELL_UP_FOR_CLOCK) { - values("120.044"); + timing_type : falling_edge; + cell_rise(CELL_TABLE) { + values("0.12, 0.121, 0.131",\ + "0.12, 0.121, 0.131",\ + "0.12, 0.121, 0.131"); } - cell_fall(CELL_DN_FOR_CLOCK) { - values("120.044"); + cell_fall(CELL_TABLE) { + values("0.12, 0.121, 0.131",\ + "0.12, 0.121, 0.131",\ + "0.12, 0.121, 0.131"); } - rise_transition(TRAN) { - values("120.044"); + rise_transition(CELL_TABLE) { + values("0.006, 0.007, 0.018",\ + "0.006, 0.007, 0.018",\ + "0.006, 0.007, 0.018"); } - fall_transition(TRAN) { - values("120.044"); + fall_transition(CELL_TABLE) { + values("0.006, 0.007, 0.018",\ + "0.006, 0.007, 0.018",\ + "0.006, 0.007, 0.018"); } } + } } bus(ADDR){ bus_type : ADDR; direction : input; capacitance : 0.2091; - max_transition : 0.5; + max_transition : 0.04; fanout_load : 1.000000; pin(ADDR[3:0]){ - } timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.015"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("0.009"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("-0.005"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.011"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } + } } pin(CSb){ @@ -217,21 +207,29 @@ cell (sram_2_16_1_freepdk45){ timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.015"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("0.009"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("-0.005"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.011"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } } @@ -242,21 +240,29 @@ cell (sram_2_16_1_freepdk45){ timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.015"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("0.009"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("-0.005"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.011"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } } @@ -267,21 +273,29 @@ cell (sram_2_16_1_freepdk45){ timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.015"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("0.009"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("-0.005"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.011"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } } @@ -293,20 +307,20 @@ cell (sram_2_16_1_freepdk45){ timing(){ timing_type :"min_pulse_width"; related_pin : clk; - rise_constraint(CLK_TRAN) { + rise_constraint(scalar) { values("0.0"); } - fall_constraint(CLK_TRAN) { + fall_constraint(scalar) { values("0.0"); } } timing(){ timing_type :"minimum_period"; related_pin : clk; - rise_constraint(CLK_TRAN) { + rise_constraint(scalar) { values("0.0"); } - fall_constraint(CLK_TRAN) { + fall_constraint(scalar) { values("0.0"); } } diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm.lib b/compiler/tests/golden/sram_2_16_1_scn3me_subm.lib index 212a38b5..958bd7ed 100644 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm.lib +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm.lib @@ -31,52 +31,18 @@ library (sram_2_16_1_scn3me_subm_lib){ default_max_fanout : 4.0 ; default_connection_class : universal ; - lu_table_template(CELL_UP_FOR_CLOCK){ + lu_table_template(CELL_TABLE){ variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; - index_1 ("0.5"); - index_2 ("0.5"); + index_1("0.0125, 0.05, 0.4"); + index_2("2.45605, 9.8242, 78.5936"); } - lu_table_template(CELL_DN_FOR_CLOCK){ - variable_1 : input_net_transition; - variable_2 : total_output_net_capacitance; - index_1 ("0.5"); - index_2 ("0.5"); - } - - lu_table_template(CONSTRAINT_HIGH_POS){ + lu_table_template(CONSTRAINT_TABLE){ variable_1 : related_pin_transition; variable_2 : constrained_pin_transition; - index_1 ("0.5"); - index_2 ("0.5"); - } - - lu_table_template(CONSTRAINT_LOW_POS){ - variable_1 : related_pin_transition; - variable_2 : constrained_pin_transition; - index_1 ("0.5"); - index_2 ("0.5"); - } - - lu_table_template(CLK_TRAN) { - variable_1 : constrained_pin_transition; - index_1 ("0.5"); - } - - lu_table_template(TRAN) { - variable_1 : total_output_net_capacitance; - index_1 ("0.5"); - } - - power_lut_template(INPUT_BY_TRANS_FOR_CLOCK){ - variable_1 : input_transition_time; - index_1 ("0.5"); - } - - power_lut_template(INPUT_BY_TRANS_FOR_SIGNAL){ - variable_1 : input_transition_time; - index_1 ("0.5"); + index_1("0.0125, 0.05, 0.4"); + index_2("0.0125, 0.05, 0.4"); } default_operating_conditions : TT; @@ -113,102 +79,126 @@ cell (sram_2_16_1_scn3me_subm){ bus(DATA){ bus_type : DATA; direction : inout; - max_capacitance : 11.3222; - pin(DATA[1:0]){ - } + max_capacitance : 78.5936; three_state : "!OEb & !clk"; memory_write(){ address : ADDR; clocked_on : clk; } + memory_read(){ + address : ADDR; + } + pin(DATA[1:0]){ internal_power(){ when : "OEb & !clk"; - rise_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("15.6576"); + rise_power(scalar){ + values("4.5249"); } - fall_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("15.6576"); + fall_power(scalar){ + values("5.117"); } } timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.093"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.082, 0.088, 0.186",\ + "0.082, 0.088, 0.186",\ + "0.082, 0.088, 0.186"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.024"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.021, 0.021, 0.027",\ + "0.021, 0.021, 0.027",\ + "0.021, 0.021, 0.027"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.046"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.015, 0.021",\ + "0.009, 0.015, 0.021",\ + "0.009, 0.015, 0.021"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.083"); + fall_constraint(CONSTRAINT_TABLE) { + values("-0.065, -0.071, -0.175",\ + "-0.065, -0.071, -0.175",\ + "-0.065, -0.071, -0.175"); } } - memory_read(){ - address : ADDR; - } internal_power(){ when : "!OEb & !clk"; - rise_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("6.2822"); + rise_power(scalar){ + values("5.8331"); } - fall_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("6.2822"); + fall_power(scalar){ + values("5.8513"); } } timing(){ timing_sense : non_unate; related_pin : "clk"; - timing_type : rising_edge; - cell_rise(CELL_UP_FOR_CLOCK) { - values("1.658"); + timing_type : falling_edge; + cell_rise(CELL_TABLE) { + values("0.262, 0.343, 1.014",\ + "0.264, 0.345, 1.018",\ + "0.311, 0.389, 1.063"); } - cell_fall(CELL_DN_FOR_CLOCK) { - values("2.364"); + cell_fall(CELL_TABLE) { + values("0.836, 0.944, 1.92",\ + "0.84, 0.948, 1.924",\ + "0.877, 0.985, 1.959"); } - rise_transition(TRAN) { - values("1.658"); + rise_transition(CELL_TABLE) { + values("0.209, 0.354, 1.883",\ + "0.213, 0.356, 1.882",\ + "0.314, 0.361, 1.884"); } - fall_transition(TRAN) { - values("2.364"); + fall_transition(CELL_TABLE) { + values("0.236, 0.444, 6.753",\ + "0.236, 0.444, 6.724",\ + "0.236, 0.444, 6.53"); } } + } } bus(ADDR){ bus_type : ADDR; direction : input; capacitance : 9.8242; - max_transition : 0.5; + max_transition : 0.4; fanout_load : 1.000000; pin(ADDR[3:0]){ - } timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.093"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.082, 0.088, 0.186",\ + "0.082, 0.088, 0.186",\ + "0.082, 0.088, 0.186"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.024"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.021, 0.021, 0.027",\ + "0.021, 0.021, 0.027",\ + "0.021, 0.021, 0.027"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.046"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.015, 0.021",\ + "0.009, 0.015, 0.021",\ + "0.009, 0.015, 0.021"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.083"); + fall_constraint(CONSTRAINT_TABLE) { + values("-0.065, -0.071, -0.175",\ + "-0.065, -0.071, -0.175",\ + "-0.065, -0.071, -0.175"); } } + } } pin(CSb){ @@ -217,21 +207,29 @@ cell (sram_2_16_1_scn3me_subm){ timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.093"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.082, 0.088, 0.186",\ + "0.082, 0.088, 0.186",\ + "0.082, 0.088, 0.186"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.024"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.021, 0.021, 0.027",\ + "0.021, 0.021, 0.027",\ + "0.021, 0.021, 0.027"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.046"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.015, 0.021",\ + "0.009, 0.015, 0.021",\ + "0.009, 0.015, 0.021"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.083"); + fall_constraint(CONSTRAINT_TABLE) { + values("-0.065, -0.071, -0.175",\ + "-0.065, -0.071, -0.175",\ + "-0.065, -0.071, -0.175"); } } } @@ -242,21 +240,29 @@ cell (sram_2_16_1_scn3me_subm){ timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.093"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.082, 0.088, 0.186",\ + "0.082, 0.088, 0.186",\ + "0.082, 0.088, 0.186"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.024"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.021, 0.021, 0.027",\ + "0.021, 0.021, 0.027",\ + "0.021, 0.021, 0.027"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.046"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.015, 0.021",\ + "0.009, 0.015, 0.021",\ + "0.009, 0.015, 0.021"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.083"); + fall_constraint(CONSTRAINT_TABLE) { + values("-0.065, -0.071, -0.175",\ + "-0.065, -0.071, -0.175",\ + "-0.065, -0.071, -0.175"); } } } @@ -267,21 +273,29 @@ cell (sram_2_16_1_scn3me_subm){ timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.093"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.082, 0.088, 0.186",\ + "0.082, 0.088, 0.186",\ + "0.082, 0.088, 0.186"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.024"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.021, 0.021, 0.027",\ + "0.021, 0.021, 0.027",\ + "0.021, 0.021, 0.027"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.046"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.015, 0.021",\ + "0.009, 0.015, 0.021",\ + "0.009, 0.015, 0.021"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.083"); + fall_constraint(CONSTRAINT_TABLE) { + values("-0.065, -0.071, -0.175",\ + "-0.065, -0.071, -0.175",\ + "-0.065, -0.071, -0.175"); } } } @@ -293,21 +307,21 @@ cell (sram_2_16_1_scn3me_subm){ timing(){ timing_type :"min_pulse_width"; related_pin : clk; - rise_constraint(CLK_TRAN) { - values("2.543"); + rise_constraint(scalar) { + values("1.328"); } - fall_constraint(CLK_TRAN) { - values("2.543"); + fall_constraint(scalar) { + values("1.328"); } } timing(){ timing_type :"minimum_period"; related_pin : clk; - rise_constraint(CLK_TRAN) { - values("5.086"); + rise_constraint(scalar) { + values("2.656"); } - fall_constraint(CLK_TRAN) { - values("5.086"); + fall_constraint(scalar) { + values("2.656"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm_analytical.lib b/compiler/tests/golden/sram_2_16_1_scn3me_subm_analytical.lib index 4e0365d9..42a49cab 100644 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm_analytical.lib @@ -31,52 +31,18 @@ library (sram_2_16_1_scn3me_subm_lib){ default_max_fanout : 4.0 ; default_connection_class : universal ; - lu_table_template(CELL_UP_FOR_CLOCK){ + lu_table_template(CELL_TABLE){ variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; - index_1 ("0.5"); - index_2 ("0.5"); + index_1("0.0125, 0.05, 0.4"); + index_2("2.45605, 9.8242, 78.5936"); } - lu_table_template(CELL_DN_FOR_CLOCK){ - variable_1 : input_net_transition; - variable_2 : total_output_net_capacitance; - index_1 ("0.5"); - index_2 ("0.5"); - } - - lu_table_template(CONSTRAINT_HIGH_POS){ + lu_table_template(CONSTRAINT_TABLE){ variable_1 : related_pin_transition; variable_2 : constrained_pin_transition; - index_1 ("0.5"); - index_2 ("0.5"); - } - - lu_table_template(CONSTRAINT_LOW_POS){ - variable_1 : related_pin_transition; - variable_2 : constrained_pin_transition; - index_1 ("0.5"); - index_2 ("0.5"); - } - - lu_table_template(CLK_TRAN) { - variable_1 : constrained_pin_transition; - index_1 ("0.5"); - } - - lu_table_template(TRAN) { - variable_1 : total_output_net_capacitance; - index_1 ("0.5"); - } - - power_lut_template(INPUT_BY_TRANS_FOR_CLOCK){ - variable_1 : input_transition_time; - index_1 ("0.5"); - } - - power_lut_template(INPUT_BY_TRANS_FOR_SIGNAL){ - variable_1 : input_transition_time; - index_1 ("0.5"); + index_1("0.0125, 0.05, 0.4"); + index_2("0.0125, 0.05, 0.4"); } default_operating_conditions : TT; @@ -113,102 +79,126 @@ cell (sram_2_16_1_scn3me_subm){ bus(DATA){ bus_type : DATA; direction : inout; - max_capacitance : 11.3222; - pin(DATA[1:0]){ - } + max_capacitance : 78.5936; three_state : "!OEb & !clk"; memory_write(){ address : ADDR; clocked_on : clk; } + memory_read(){ + address : ADDR; + } + pin(DATA[1:0]){ internal_power(){ when : "OEb & !clk"; - rise_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("0.0"); + rise_power(scalar){ + values("0"); } - fall_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("0.0"); + fall_power(scalar){ + values("0"); } } timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.093"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.024"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.046"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.083"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } - memory_read(){ - address : ADDR; - } internal_power(){ when : "!OEb & !clk"; - rise_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("0.0"); + rise_power(scalar){ + values("0"); } - fall_power(INPUT_BY_TRANS_FOR_SIGNAL){ - values("0.0"); + fall_power(scalar){ + values("0"); } } timing(){ timing_sense : non_unate; related_pin : "clk"; - timing_type : rising_edge; - cell_rise(CELL_UP_FOR_CLOCK) { - values("553.907"); + timing_type : falling_edge; + cell_rise(CELL_TABLE) { + values("0.57, 0.617, 1.058",\ + "0.57, 0.617, 1.058",\ + "0.57, 0.617, 1.058"); } - cell_fall(CELL_DN_FOR_CLOCK) { - values("553.907"); + cell_fall(CELL_TABLE) { + values("0.57, 0.617, 1.058",\ + "0.57, 0.617, 1.058",\ + "0.57, 0.617, 1.058"); } - rise_transition(TRAN) { - values("553.907"); + rise_transition(CELL_TABLE) { + values("0.024, 0.081, 0.61",\ + "0.024, 0.081, 0.61",\ + "0.024, 0.081, 0.61"); } - fall_transition(TRAN) { - values("553.907"); + fall_transition(CELL_TABLE) { + values("0.024, 0.081, 0.61",\ + "0.024, 0.081, 0.61",\ + "0.024, 0.081, 0.61"); } } + } } bus(ADDR){ bus_type : ADDR; direction : input; capacitance : 9.8242; - max_transition : 0.5; + max_transition : 0.4; fanout_load : 1.000000; pin(ADDR[3:0]){ - } timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.093"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.024"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.046"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.083"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } + } } pin(CSb){ @@ -217,21 +207,29 @@ cell (sram_2_16_1_scn3me_subm){ timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.093"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.024"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.046"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.083"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } } @@ -242,21 +240,29 @@ cell (sram_2_16_1_scn3me_subm){ timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.093"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.024"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.046"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.083"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } } @@ -267,21 +273,29 @@ cell (sram_2_16_1_scn3me_subm){ timing(){ timing_type : setup_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.093"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.024"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk"; - rise_constraint(CONSTRAINT_HIGH_POS) { - values("0.046"); + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } - fall_constraint(CONSTRAINT_LOW_POS) { - values("-0.083"); + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } } @@ -293,20 +307,20 @@ cell (sram_2_16_1_scn3me_subm){ timing(){ timing_type :"min_pulse_width"; related_pin : clk; - rise_constraint(CLK_TRAN) { + rise_constraint(scalar) { values("0.0"); } - fall_constraint(CLK_TRAN) { + fall_constraint(scalar) { values("0.0"); } } timing(){ timing_type :"minimum_period"; related_pin : clk; - rise_constraint(CLK_TRAN) { + rise_constraint(scalar) { values("0.0"); } - fall_constraint(CLK_TRAN) { + fall_constraint(scalar) { values("0.0"); } } diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index d6704af2..9588dccd 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -59,24 +59,24 @@ def isapproxdiff(f1, f2, error_tolerance=0.001): # 2. Remove the floats from the string for f in b1_floats: - b1=b1.replace(str(f),"") + b1=b1.replace(str(f),"",1) for f in b2_floats: - b2=b2.replace(str(f),"") + b2=b2.replace(str(f),"",1) #print "b1:",b1, #print "b2:",b2, # 3. Check if remaining string matches if b1 != b2: - debug.info(2,"Line: {0}\n!=\nLine: {1}".format(b1,b2)) + debug.info(1,"Line: {0}\n!=\nLine: {1}".format(b1,b2)) return False # 4. Now compare that the floats match if len(b1_floats)!=len(b2_floats): - debug.info(2,"Len {0} != {1}".format(len(b1_floats),len(b2_floats))) + debug.info(1,"Len {0} != {1}".format(len(b1_floats),len(b2_floats))) return False for (f1,f2) in zip(b1_floats,b2_floats): if not relative_compare(float(f1),float(f2),error_tolerance): - debug.info(2, "Float {0} != {1}".format(f1,f2)) + debug.info(1, "Float {0} != {1}".format(f1,f2)) return False if not b1: diff --git a/compiler/tri_gate.py b/compiler/tri_gate.py index 2a98c2fd..d5b728c9 100644 --- a/compiler/tri_gate.py +++ b/compiler/tri_gate.py @@ -21,11 +21,11 @@ class tri_gate(design.design): self.width = tri_gate.chars["width"] self.height = tri_gate.chars["height"] - def delay(self, slope, load=0.0): + def delay(self, slew, load=0.0): from tech import spice r = spice["min_tx_r"] - c_para = spice["min_tx_c_para"]#ff - return self.cal_delay_with_rc(r = r, c = c_para+load, slope =slope) + c_para = spice["min_tx_drain_c"] + return self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew) def input_load(self): diff --git a/compiler/tri_gate_array.py b/compiler/tri_gate_array.py index 8a187ad2..6fa86e4f 100644 --- a/compiler/tri_gate_array.py +++ b/compiler/tri_gate_array.py @@ -35,9 +35,9 @@ class tri_gate_array(design.design): def add_pins(self): """create the name of pins depend on the word size""" for i in range(self.word_size): - self.add_pin("tri_in[{0}]".format(i)) + self.add_pin("IN[{0}]".format(i)) for i in range(self.word_size): - self.add_pin("DATA[{0}]".format(i)) + self.add_pin("OUT[{0}]".format(i)) for pin in ["en", "en_bar", "vdd", "gnd"]: self.add_pin(pin) @@ -49,8 +49,8 @@ class tri_gate_array(design.design): self.tri_gate_positions = [] self.vdd_positions = [] self.gnd_positions = [] - self.tri_in_positions = [] - self.DATA_positions = [] + self.in_positions = [] + self.out_positions = [] def add_modules(self): """instantiation of a tri gate""" @@ -75,8 +75,8 @@ class tri_gate_array(design.design): mod=self.tri, offset=[x_off, 0], mirror = mirror) - self.connect_inst(["tri_in[{0}]".format(i), - "DATA[{0}]".format(i), + self.connect_inst(["in[{0}]".format(i), + "out[{0}]".format(i), "en", "en_bar", "vdd", "gnd"]) def add_metal_rails(self): @@ -118,20 +118,18 @@ class tri_gate_array(design.design): self.add_label(text="gnd", layer="metal2", offset=pin_offset["gnd"]) - self.add_label(text="tri_in[{0}]".format(i), + self.add_label(text="in[{0}]".format(i), layer="metal2", offset=pin_offset["in"]) - self.add_label(text="DATA[{0}]".format(i), + self.add_label(text="out[{0}]".format(i), layer="metal2", offset=pin_offset["out"]) self.vdd_positions.append(pin_offset["vdd"]) self.gnd_positions.append(pin_offset["gnd"]) - self.tri_in_positions.append(pin_offset["in"]) - self.DATA_positions.append(pin_offset["out"]) + self.in_positions.append(pin_offset["in"]) + self.out_positions.append(pin_offset["out"]) - - def delay(self, slope, load=0.0): - result = self.tri.delay(slope = slope, - load = load) + def delay(self, slew, load=0.0): + result = self.tri.delay(slew = slew, load = load) return result diff --git a/compiler/wordline_driver.py b/compiler/wordline_driver.py index 911cc627..10ef1984 100644 --- a/compiler/wordline_driver.py +++ b/compiler/wordline_driver.py @@ -210,12 +210,12 @@ class wordline_driver(design.design): self.vdd_positions.append(vdd_offset) self.gnd_positions.append(gnd_offset) - def delay(self, slope, load=0): + def delay(self, slew, load=0): # decode_out -> net - decode_t_net = self.NAND2.delay(slope = slope, load = self.inv.input_load()) + decode_t_net = self.NAND2.delay(slew, self.inv.input_load()) # net -> wl - net_t_wl = self.inv.delay(slope = decode_t_net.slope, load = load) + net_t_wl = self.inv.delay(decode_t_net.slew, load) result = decode_t_net + net_t_wl return result diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index ef7f952c..4b1ba2e5 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -260,11 +260,11 @@ spice["fet_models"] = [SPICE_MODEL_DIR+"/NMOS_VTG.inc", SPICE_MODEL_DIR+"/PMOS_VTG.inc"] #spice stimulus related variables -spice["clock_period"] = 2.0 +spice["feasible_period"] = 5 # estimated feasible period in ns spice["supply_voltage"] = 1.0 #vdd in [Volts] spice["gnd_voltage"] = 0.0 #gnd in [Volts] -spice["rise_time"] = 0.001 #rise time in [Nano-seconds] -spice["fall_time"] = 0.001 #fall time in [Nano-seconds] +spice["rise_time"] = 0.005 #rise time in [Nano-seconds] +spice["fall_time"] = 0.005 #fall time in [Nano-seconds] spice["temp"] = 25 #temperature in [Celsius] #parasitics of metal for bit/word lines @@ -272,8 +272,8 @@ spice["bitline_res"] = 0.1 #bitline resistance in [Ohms/micro-meter] spice["bitline_cap"] = 0.2 #bitline capacitance in [Femto-farad/micro-meter] spice["wordline_res"] = 0.1 #wordline resistance in [Ohms/micro-meter] spice["wordline_cap"] = 0.2 #wordline capacitance in [Femto-farad/micro-meter] -spice["FF_in_cap"] = 0.2091 #Input capacitance of ms_flop (Din) [Femto-farad] -spice["tri_gate_out_cap"] = 0.41256 #Output capacitance of tri_gate (tri_out) [Femto-farad] +spice["FF_in_cap"] = 0.2091 #Input capacitance of ms_flop (Din) [Femto-farad] +spice["tri_gate_out_cap"] = 0.41256 #Output capacitance of tri_gate (tri_out) [Femto-farad] #sram signal names @@ -288,14 +288,13 @@ spice["minwidth_tx"] = drc["minwidth_tx"] spice["channel"] = drc["minlength_channel"] spice["clk"] = "clk" -# estimated feasible period in ns -spice["feasible_period"] = 5 - -# analytical delay parameter -spice["wire_unit_r"] = 0.075 #ohm -spice["wire_unit_c"] = 0.64 #ff/um^2 -spice["min_tx_r"] = 9250.0 -spice["min_tx_c_para"] = 0.7 #ff -spice["min_tx_gate_c"] = 0.2 -spice["msflop_delay"] = 20.5171565446#ps -spice["msflop_slope"] = 13.0801872972#ps +# analytical delay parameters +spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square +spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2 +spice["min_tx_r"] = 9250.0 # Minimum transistor on resistance in ohms +spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff +spice["min_tx_gate_c"] = 0.2 # Minimum transistor gate capacitance in ff +spice["msflop_setup"] = 9 # DFF setup time in ps +spice["msflop_hold"] = 1 # DFF hold time in ps +spice["msflop_delay"] = 20.5 # DFF Clk-to-q delay in ps +spice["msflop_slew"] = 13.1 # DFF output slew in ps w/ no load diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index 72eda0e8..6c379e67 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -178,11 +178,11 @@ spice["pmos"]="p" spice["fet_models"] = [os.environ.get("SPICE_MODEL_DIR")+"/on_c5n.sp"] #spice stimulus related variables -spice["clock_period"] = 10.0 +spice["feasible_period"] = 5 # estimated feasible period in ns spice["supply_voltage"] = 5.0 #vdd in [Volts] spice["gnd_voltage"] = 0.0 #gnd in [Volts] -spice["rise_time"] = 0.001 #rise time in [Nano-seconds] -spice["fall_time"] = 0.001 #fall time in [Nano-seconds] +spice["rise_time"] = 0.05 #rise time in [Nano-seconds] +spice["fall_time"] = 0.05 #fall time in [Nano-seconds] spice["temp"] = 25 #temperature in [Celsius] #parasitics of metal for bit/word lines @@ -190,8 +190,8 @@ spice["bitline_res"] = 0.1 #bitline resistance in [Ohms/micro-meter] spice["bitline_cap"] = 0.2 #bitline capacitance in [Femto-farad/micro-meter] spice["wordline_res"] = 0.1 #wordline resistance in [Ohms/micro-meter] spice["wordline_cap"] = 0.2 #wordline capacitance in [Femto-farad/micro-meter] -spice["FF_in_cap"] = 9.8242 #Input capacitance of ms_flop (Din) [Femto-farad] -spice["tri_gate_out_cap"] = 1.4980 #Output capacitance of tri_gate (tri_out) [Femto-farad] +spice["FF_in_cap"] = 9.8242 #Input capacitance of ms_flop (Din) [Femto-farad] +spice["tri_gate_out_cap"] = 1.4980 #Output capacitance of tri_gate (tri_out) [Femto-farad] #sram signal names @@ -206,16 +206,15 @@ spice["minwidth_tx"] = drc["minwidth_tx"] spice["channel"] = drc["minlength_channel"] spice["clk"] = "clk" -# estimated feasible period in ns -spice["feasible_period"] = 5 +# analytical delay parameters +# FIXME: These need to be updated for SCMOS, they are copied from FreePDK45. +spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square +spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2 +spice["min_tx_r"] = 9250.0 # Minimum transistor on resistance in ohms +spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff +spice["min_tx_gate_c"] = 0.1 # Minimum transistor gate capacitance in ff +spice["msflop_setup"] = 9 # DFF setup time in ps +spice["msflop_hold"] = 1 # DFF hold time in ps +spice["msflop_delay"] = 20.5 # DFF Clk-to-q delay in ps +spice["msflop_slew"] = 13.1 # DFF output slew in ps w/ no load -# analytical delay parameter -# these numbers are copied from freepdk -# need to measure them in scn cmos -spice["wire_unit_r"] = 0.075 #ohm -spice["wire_unit_c"] = 0.64 #ff/um^2 -spice["min_tx_r"] = 9250.0 -spice["min_tx_c_para"] = 0.7 #ff -spice["min_tx_gate_c"] = 0.1 -spice["msflop_delay"] = 20.5171565446#ps -spice["msflop_slope"] = 13.0801872972#ps