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