From 1a2865b9b18dc557a418b4a8a173ac1e9ca8a7d2 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 14 May 2021 17:07:00 -0700 Subject: [PATCH 01/36] Add Xyce tests --- compiler/tests/21_xyce_delay_test.py | 102 +++++++++++++++++++++++ compiler/tests/21_xyce_setuphold_test.py | 67 +++++++++++++++ 2 files changed, 169 insertions(+) create mode 100755 compiler/tests/21_xyce_delay_test.py create mode 100755 compiler/tests/21_xyce_setuphold_test.py diff --git a/compiler/tests/21_xyce_delay_test.py b/compiler/tests/21_xyce_delay_test.py new file mode 100755 index 00000000..04a81886 --- /dev/null +++ b/compiler/tests/21_xyce_delay_test.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class timing_sram_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + OPTS.spice_name="xyce" + OPTS.analytical_delay = False + OPTS.netlist_only = True + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import delay + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=16, + num_banks=1) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") + s = factory.create(module_type="sram", sram_config=c) + + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + probe_address = "1" * s.s.addr_size + probe_data = s.s.word_size - 1 + 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) + import tech + loads = [tech.spice["dff_in_cap"]*4] + slews = [tech.spice["rise_time"]*2] + data, port_data = d.analyze(probe_address, probe_data, slews, loads) + # Combine info about port into all data + data.update(port_data[0]) + + if OPTS.tech_name == "freepdk45": + golden_data = {'delay_hl': [0.24042560000000002], + 'delay_lh': [0.24042560000000002], + 'disabled_read0_power': [0.8981647999999998], + 'disabled_read1_power': [0.9101543999999998], + 'disabled_write0_power': [0.9270382999999998], + 'disabled_write1_power': [0.9482969999999998], + 'leakage_power': 2.9792199999999998, + 'min_period': 0.938, + 'read0_power': [1.1107930999999998], + 'read1_power': [1.1143252999999997], + 'slew_hl': [0.2800772], + 'slew_lh': [0.2800772], + 'write0_power': [1.1667769], + 'write1_power': [1.0986076999999999]} + elif OPTS.tech_name == "scn4m_subm": + golden_data = {'delay_hl': [1.884186], + 'delay_lh': [1.884186], + 'disabled_read0_power': [20.86336], + 'disabled_read1_power': [22.10636], + 'disabled_write0_power': [22.62321], + 'disabled_write1_power': [23.316010000000002], + 'leakage_power': 13.351170000000002, + 'min_period': 7.188, + 'read0_power': [29.90159], + 'read1_power': [30.47858], + 'slew_hl': [2.042723], + 'slew_lh': [2.042723], + 'write0_power': [32.13199], + 'write1_power': [28.46703]} + 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())) + + self.assertTrue(self.check_golden_data(data,golden_data,0.25)) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/21_xyce_setuphold_test.py b/compiler/tests/21_xyce_setuphold_test.py new file mode 100755 index 00000000..f53212f8 --- /dev/null +++ b/compiler/tests/21_xyce_setuphold_test.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS + + +class timing_setup_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + OPTS.spice_name="Xyce" + OPTS.analytical_delay = False + OPTS.netlist_only = True + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import setup_hold + import tech + slews = [tech.spice["rise_time"]*2] + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + sh = setup_hold(corner) + data = sh.analyze(slews,slews) + if OPTS.tech_name == "freepdk45": + golden_data = {'hold_times_HL': [-0.0158691], + 'hold_times_LH': [-0.0158691], + 'setup_times_HL': [0.026855499999999997], + 'setup_times_LH': [0.032959]} + elif OPTS.tech_name == "scn4m_subm": + golden_data = {'hold_times_HL': [-0.0805664], + 'hold_times_LH': [-0.11718749999999999], + 'setup_times_HL': [0.16357419999999998], + 'setup_times_LH': [0.1757812]} + elif OPTS.tech_name == "sky130": + golden_data = {'hold_times_HL': [-0.05615234], + 'hold_times_LH': [-0.03173828], + 'setup_times_HL': [0.078125], + 'setup_times_LH': [0.1025391]} + 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())) + + self.assertTrue(self.check_golden_data(data,golden_data,0.25)) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From 38322dae4e5ddf97d54d97b81cb446e256eeea96 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 18 May 2021 14:58:57 -0700 Subject: [PATCH 02/36] Xyce can be capital or lower case --- compiler/characterizer/__init__.py | 2 +- compiler/characterizer/charutils.py | 2 +- compiler/characterizer/stimuli.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index d5bcdbc6..a092ac1e 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -34,7 +34,7 @@ if not OPTS.analytical_delay: else: (OPTS.spice_name, OPTS.spice_exe) = get_tool("spice", ["Xyce", "ngspice", "ngspice.exe", "hspice", "xa"]) - if OPTS.spice_name == "Xyce": + if OPTS.spice_name in ["Xyce", "xyce"]: (OPTS.mpi_name, OPTS.mpi_exe) = get_tool("mpi", ["mpirun"]) OPTS.hier_seperator = ":" else: diff --git a/compiler/characterizer/charutils.py b/compiler/characterizer/charutils.py index b25093a0..59ef3177 100644 --- a/compiler/characterizer/charutils.py +++ b/compiler/characterizer/charutils.py @@ -26,7 +26,7 @@ def parse_spice_list(filename, key): full_filename="{0}xa.meas".format(OPTS.openram_temp) elif OPTS.spice_name == "spectre": full_filename = os.path.join(OPTS.openram_temp, "delay_stim.measure") - elif OPTS.spice_name == "Xyce": + elif OPTS.spice_name in ["Xyce", "xyce"]: full_filename = os.path.join(OPTS.openram_temp, "spice_stdout.log") else: # ngspice/hspice using a .lis file diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index f49f636b..55bdfd09 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -271,7 +271,7 @@ class stimuli(): self.sf.write(".OPTIONS POST=1 RUNLVL={0} PROBE\n".format(runlvl)) self.sf.write(".OPTIONS PSF=1 \n") self.sf.write(".OPTIONS HIER_DELIM=1 \n") - elif OPTS.spice_name == "Xyce": + elif OPTS.spice_name in ["Xyce", "xyce"]: self.sf.write(".OPTIONS DEVICE TEMP={}\n".format(self.temperature)) self.sf.write(".OPTIONS MEASURE MEASFAIL=1\n") self.sf.write(".TRAN {0}p {1}n\n".format(timestep, end_time)) @@ -318,7 +318,7 @@ class stimuli(): # Adding a commented out supply for simulators where gnd and 0 are not global grounds. self.sf.write("\n*Nodes gnd and 0 are the same global ground node in ngspice/hspice/xa. Otherwise, this source may be needed.\n") - if OPTS.spice_name == "Xyce": + if OPTS.spice_name in ["Xyce", "xyce"]: self.sf.write("V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0)) else: self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0)) @@ -358,7 +358,7 @@ class stimuli(): temp_stim, OPTS.openram_temp) valid_retcode=0 - elif OPTS.spice_name == "Xyce": + elif OPTS.spice_name in ["Xyce", "xyce"]: if OPTS.num_sim_threads > 1 and OPTS.mpi_name: mpi_cmd = "{0} -np {1}".format(OPTS.mpi_exe, OPTS.num_sim_threads) From 1274824793784ee368bf3bab1b6842a0a867e01b Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 21 May 2021 11:27:15 -0700 Subject: [PATCH 03/36] Restrict to direct KLU solver --- compiler/characterizer/stimuli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 55bdfd09..49fbc97c 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -274,6 +274,7 @@ class stimuli(): elif OPTS.spice_name in ["Xyce", "xyce"]: self.sf.write(".OPTIONS DEVICE TEMP={}\n".format(self.temperature)) self.sf.write(".OPTIONS MEASURE MEASFAIL=1\n") + self.sf.write(".OPTIONS LINSOL type=klu\n") self.sf.write(".TRAN {0}p {1}n\n".format(timestep, end_time)) else: debug.error("Unkown spice simulator {}".format(OPTS.spice_name)) From 8964abc2b7201f2750af0ba5f7ff281597ddaf6a Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 9 Jun 2021 16:02:32 -0700 Subject: [PATCH 04/36] Change simulator based on one in use. --- compiler/characterizer/stimuli.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 80ddf4fc..edbb767a 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -299,6 +299,7 @@ class stimuli(): self.sf.write("* {} process corner\n".format(self.process)) for item in self.device_libraries: + item[0] = item[0].replace("SIMULATOR", OPTS.spice_name.lower()) if os.path.isfile(item[0]): self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1])) else: @@ -307,6 +308,7 @@ class stimuli(): includes = self.device_models + [circuit] for item in list(includes): + item = item.replace("SIMULATOR", OPTS.spice_name.lower()) self.sf.write(".include \"{0}\"\n".format(item)) def add_comment(self, msg): From 10f561648f306b48706f75c4a3f5ae61b91e880d Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 9 Jun 2021 18:24:21 -0700 Subject: [PATCH 05/36] remove hierarchical decoder vertial m1 above pins --- compiler/modules/hierarchical_decoder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 141e717a..560d0f6b 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -662,9 +662,9 @@ class hierarchical_decoder(design.design): mid_point2 = vector(x_offset, y_offset) rail_pos = vector(self.predecode_bus[rail_name].cx(), mid_point2.y) self.add_path(self.output_layer, [pin_pos, mid_point1, mid_point2, rail_pos]) - if layer_props.hierarchical_decoder.vertical_supply: - above_rail = vector(self.predecode_bus[rail_name].cx(), mid_point2.y + (self.cell_height / 2)) - self.add_path(self.bus_layer, [rail_pos, above_rail], width=self.li_width + self.m1_enclose_mcon * 2) + #if layer_props.hierarchical_decoder.vertical_supply: + # above_rail = vector(self.predecode_bus[rail_name].cx(), mid_point2.y + (self.cell_height / 2)) + # self.add_path(self.bus_layer, [rail_pos, above_rail], width=self.li_width + self.m1_enclose_mcon * 2) # pin_pos = pin.center() # rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y) From 2e72da0e53982b74279d5e4a6639c744ccaf9083 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 10 Jun 2021 14:01:28 -0700 Subject: [PATCH 06/36] rotate input to rail contacts for drc --- compiler/modules/hierarchical_predecode.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 386f9bb6..a9dacb1d 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -215,12 +215,16 @@ class hierarchical_predecode(design.design): in_pos = vector(self.input_rails[in_pin].cx(), y_offset) a_pos = vector(self.decode_rails[a_pin].cx(), y_offset) self.add_path(self.input_layer, [in_pos, a_pos]) + self.add_via_stack_center(from_layer=self.input_layer, to_layer=self.bus_layer, - offset=[self.input_rails[in_pin].cx(), y_offset]) + offset=[self.input_rails[in_pin].cx(), y_offset], + directions= ("H", "H")) + self.add_via_stack_center(from_layer=self.input_layer, to_layer=self.bus_layer, - offset=[self.decode_rails[a_pin].cx(), y_offset]) + offset=[self.decode_rails[a_pin].cx(), y_offset], + directions=("H", "H")) def route_output_ands(self): """ From bee9b07516349d2e5bb608abc168c27b92a6f0a4 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 11 Jun 2021 18:19:07 -0700 Subject: [PATCH 07/36] fix decoder routing --- compiler/modules/hierarchical_decoder.py | 73 ++++++++++++++++++---- compiler/modules/hierarchical_predecode.py | 5 +- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 560d0f6b..7eee7b69 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -11,8 +11,10 @@ import math from sram_factory import factory from vector import vector from globals import OPTS +from tech import layer_indices +from tech import layer_stacks from tech import layer_properties as layer_props - +from tech import drc class hierarchical_decoder(design.design): """ @@ -29,7 +31,7 @@ class hierarchical_decoder(design.design): b = factory.create(module_type=OPTS.bitcell) self.cell_height = b.height - + self.predecode_bus_rail_pos = [] self.num_outputs = num_outputs self.num_inputs = math.ceil(math.log(self.num_outputs, 2)) (self.no_of_pre2x4, self.no_of_pre3x8, self.no_of_pre4x16)=self.determine_predecodes(self.num_inputs) @@ -504,9 +506,9 @@ class hierarchical_decoder(design.design): offset=vector(self.bus_pitch, 0), names=input_bus_names, length=self.height) - - self.route_predecodes_to_bus() self.route_bus_to_decoder() + self.route_predecodes_to_bus() + def route_predecodes_to_bus(self): """ @@ -521,7 +523,7 @@ class hierarchical_decoder(design.design): pin = self.pre2x4_inst[pre_num].get_pin(out_name) x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch y_offset = self.pre2x4_inst[pre_num].by() + i * self.cell_height - self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) + self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset, "pre2x4") # FIXME: convert to connect_bus for pre_num in range(self.no_of_pre3x8): @@ -531,7 +533,7 @@ class hierarchical_decoder(design.design): pin = self.pre3x8_inst[pre_num].get_pin(out_name) x_offset = self.pre3x8_inst[pre_num].rx() + self.output_layer_pitch y_offset = self.pre3x8_inst[pre_num].by() + i * self.cell_height - self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) + self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset, "pre3x8") # FIXME: convert to connect_bus for pre_num in range(self.no_of_pre4x16): @@ -541,7 +543,7 @@ class hierarchical_decoder(design.design): pin = self.pre4x16_inst[pre_num].get_pin(out_name) x_offset = self.pre4x16_inst[pre_num].rx() + self.output_layer_pitch y_offset = self.pre4x16_inst[pre_num].by() + i * self.cell_height - self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) + self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset, "pre4x16") def route_bus_to_decoder(self): """ @@ -649,8 +651,9 @@ class hierarchical_decoder(design.design): to_layer=self.input_layer, offset=pin_pos, directions=("H", "H")) - - def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset): + self.predecode_bus_rail_pos.append(rail_pos) + + def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset, predecode_type): """ Connect the routing rail to the given metal1 pin using a jog to the right of the cell at the given x_offset. @@ -661,14 +664,58 @@ class hierarchical_decoder(design.design): mid_point1 = vector(x_offset, pin_pos.y) mid_point2 = vector(x_offset, y_offset) rail_pos = vector(self.predecode_bus[rail_name].cx(), mid_point2.y) - self.add_path(self.output_layer, [pin_pos, mid_point1, mid_point2, rail_pos]) + #self.add_path(self.output_layer, [pin_pos, mid_point1, mid_point2, rail_pos]) #if layer_props.hierarchical_decoder.vertical_supply: # above_rail = vector(self.predecode_bus[rail_name].cx(), mid_point2.y + (self.cell_height / 2)) # self.add_path(self.bus_layer, [rail_pos, above_rail], width=self.li_width + self.m1_enclose_mcon * 2) - # pin_pos = pin.center() - # rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y) - # self.add_path(self.output_layer, [pin_pos, rail_pos]) + #pin_pos = pin.center() + #rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y) + #self.add_path(self.output_layer, [pin_pos, rail_pos]) + + # create via for dimensions + from_layer = self.output_layer + to_layer = self.bus_layer + + cur_layer = from_layer + from_id = layer_indices[cur_layer] + to_id = layer_indices[to_layer] + + if from_id < to_id: # grow the stack up + search_id = 0 + next_id = 2 + else: # grow the stack down + search_id = 2 + next_id = 0 + curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None) + via = factory.create(module_type="contact", + layer_stack=curr_stack, + dimensions=[1, 1], + directions=self.bus_directions) + overlapping_pin_space = drc["{0}_to_{0}".format(self.output_layer)] + total_buffer_space = (overlapping_pin_space + via.height) + while(True): + drc_error = 0 + for and_input in self.predecode_bus_rail_pos: + if and_input.x == rail_pos.x: + if (abs(y_offset - and_input.y) < total_buffer_space) or (abs(y_offset - and_input.y) < via.height): + drc_error = 1 + if drc_error == 0: + break + else: + y_offset += drc["grid"] + rail_pos.y = y_offset + + if predecode_type == "pre2x4": + right_pos = pin_pos + elif predecode_type =="pre3x8": + right_pos = pin_pos + elif predecode_type == "pre4x16": + right_pos = pin_pos + # else: + # error("invalid predcoder type {}".format(predecode_type)) + self.add_path(self.output_layer, [pin_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) + self.add_via_stack_center(from_layer=pin.layer, to_layer=self.output_layer, offset=pin_pos) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index a9dacb1d..d3843f9a 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -195,7 +195,7 @@ class hierarchical_predecode(design.design): def route_inputs_to_rails(self): """ Route the uninverted inputs to the second set of rails """ - + top_and_gate = self.and_inst[-1] for num in range(self.number_of_inputs): if num == 0: @@ -321,7 +321,6 @@ class hierarchical_predecode(design.design): y_offset += drc["grid"] rail_pos.y = y_offset right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0) - self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) self.add_via_stack_center(from_layer=inv_out_pin.layer, @@ -336,7 +335,7 @@ class hierarchical_predecode(design.design): """ Route the different permutations of the NAND/AND decocer cells. """ - + # This 2D array defines the connection mapping and_input_line_combination = self.get_and_input_line_combination() for k in range(self.number_of_outputs): From 73cc6b389155d44d09cc15e9cf7e83d406ea5ae6 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 11 Jun 2021 18:20:36 -0700 Subject: [PATCH 08/36] uncomment 4x16 decoder --- compiler/tests/06_hierarchical_decoder_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index f61f365c..6cc17ee3 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -58,9 +58,9 @@ class hierarchical_decoder_test(openram_test): self.local_check(a) # Checks 3 x 4x16 and 4-input NAND decoder - # debug.info(1, "Testing 4096 row sample for hierarchical_decoder") - # a = factory.create(module_type="hierarchical_decoder", num_outputs=4096) - # self.local_check(a) + debug.info(1, "Testing 4096 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=4096) + self.local_check(a) globals.end_openram() From 25bc1781325743ae8e5ebf06a1c128d6575cebd3 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Mon, 14 Jun 2021 15:13:17 -0700 Subject: [PATCH 09/36] extend input rail --- compiler/modules/hierarchical_predecode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index d3843f9a..93c4b8d7 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -122,7 +122,7 @@ class hierarchical_predecode(design.design): self.input_rails = self.create_vertical_bus(layer=self.bus_layer, offset=offset, names=input_names, - length=self.height - 2 * self.bus_pitch, + length=self.height - self.bus_pitch, pitch=self.bus_pitch) invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)] @@ -132,7 +132,7 @@ class hierarchical_predecode(design.design): self.decode_rails = self.create_vertical_bus(layer=self.bus_layer, offset=offset, names=decode_names, - length=self.height - 2 * self.bus_pitch, + length=self.height - self.bus_pitch, pitch=self.bus_pitch) def create_input_inverters(self): From 9d7029dadc0566ec0c7f263678a2a6cc78380bd9 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Jun 2021 10:43:04 -0700 Subject: [PATCH 10/36] Only replace simulator if it is defined. --- compiler/characterizer/stimuli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index edbb767a..dae07048 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -299,7 +299,8 @@ class stimuli(): self.sf.write("* {} process corner\n".format(self.process)) for item in self.device_libraries: - item[0] = item[0].replace("SIMULATOR", OPTS.spice_name.lower()) + if OPTS.spice_name: + item[0] = item[0].replace("SIMULATOR", OPTS.spice_name.lower()) if os.path.isfile(item[0]): self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1])) else: From 6ac082ce239db4cec7ef5e21c0ec7fd9a701e201 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Jun 2021 10:44:13 -0700 Subject: [PATCH 11/36] Only replace simulator if it is defined. --- compiler/characterizer/stimuli.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index edbb767a..384a9f4c 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -299,7 +299,8 @@ class stimuli(): self.sf.write("* {} process corner\n".format(self.process)) for item in self.device_libraries: - item[0] = item[0].replace("SIMULATOR", OPTS.spice_name.lower()) + if OPTS.spice_name: + item[0] = item[0].replace("SIMULATOR", OPTS.spice_name.lower()) if os.path.isfile(item[0]): self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1])) else: @@ -308,7 +309,8 @@ class stimuli(): includes = self.device_models + [circuit] for item in list(includes): - item = item.replace("SIMULATOR", OPTS.spice_name.lower()) + if OPTS.spice_name: + item = item.replace("SIMULATOR", OPTS.spice_name.lower()) self.sf.write(".include \"{0}\"\n".format(item)) def add_comment(self, msg): From 2b9df2ff1f1b2e7e88ba13c0b61334d6640268ec Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 16 Jun 2021 11:23:27 -0700 Subject: [PATCH 12/36] uncomment function sim and datasheet generation --- compiler/sram/sram.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 1f54b440..3d448bb8 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -84,9 +84,9 @@ class sram(): debug.print_raw("SP: Writing to {0}".format(spname)) self.sp_write(spname) functional(self.s, - os.path.basename(spname), - cycles=200, - output_path=OPTS.output_path) + os.path.basename(spname), + cycles=200, + output_path=OPTS.output_path) print_time("Spice writing", datetime.datetime.now(), start_time) if not OPTS.netlist_only: @@ -142,7 +142,7 @@ class sram(): start_time = datetime.datetime.now() from characterizer import lib debug.print_raw("LIB: Characterizing... ") - lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file) + lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file) print_time("Characterization", datetime.datetime.now(), start_time) # Write the config file @@ -157,7 +157,7 @@ class sram(): from datasheet_gen import datasheet_gen dname = OPTS.output_path + self.s.name + ".html" debug.print_raw("Datasheet: Writing to {0}".format(dname)) - datasheet_gen.datasheet_write(dname) + datasheet_gen.datasheet_write(dname) print_time("Datasheet", datetime.datetime.now(), start_time) # Write a verilog model From e775f7a355a5bbb09cd9bcc6a228ee575a8c688d Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 16 Jun 2021 12:36:00 -0700 Subject: [PATCH 13/36] fixed indent --- compiler/sram/sram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 3d448bb8..17107860 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -142,7 +142,7 @@ class sram(): start_time = datetime.datetime.now() from characterizer import lib debug.print_raw("LIB: Characterizing... ") - lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file) + lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file) print_time("Characterization", datetime.datetime.now(), start_time) # Write the config file @@ -157,7 +157,7 @@ class sram(): from datasheet_gen import datasheet_gen dname = OPTS.output_path + self.s.name + ".html" debug.print_raw("Datasheet: Writing to {0}".format(dname)) - datasheet_gen.datasheet_write(dname) + datasheet_gen.datasheet_write(dname) print_time("Datasheet", datetime.datetime.now(), start_time) # Write a verilog model From 16e658726e68183d4b25a898fab16e7486dbadfe Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 16 Jun 2021 17:04:02 -0700 Subject: [PATCH 14/36] When determining bitline names, added a technology check for sky130. --- compiler/base/hierarchy_spice.py | 2 +- compiler/bitcells/bitcell_base.py | 3 +-- compiler/characterizer/simulation.py | 6 +++++- compiler/modules/replica_column.py | 3 +-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 2f2d3ec9..eca98eaf 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -32,7 +32,6 @@ class spice(): # This gets set in both spice and layout so either can be called first. self.name = name self.cell_name = cell_name - self.sp_file = OPTS.openram_tech + "sp_lib/" + cell_name + ".sp" # If we have a separate lvs directory, then all the lvs files @@ -570,6 +569,7 @@ class spice(): net = net.lower() int_net = self.name_dict[net]['int_net'] int_mod = self.name_dict[net]['mod'] + if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set): aliases.append(net) return aliases diff --git a/compiler/bitcells/bitcell_base.py b/compiler/bitcells/bitcell_base.py index 5112642e..1161fec8 100644 --- a/compiler/bitcells/bitcell_base.py +++ b/compiler/bitcells/bitcell_base.py @@ -26,7 +26,6 @@ class bitcell_base(design.design): self.nets_match = self.do_nets_exist(prop.storage_nets) self.mirror = prop.mirror self.end_caps = prop.end_caps - def get_stage_effort(self, load): parasitic_delay = 1 # This accounts for bitline being drained @@ -84,7 +83,7 @@ class bitcell_base(design.design): return self.storage_nets else: fmt_str = "Storage nodes={} not found in spice file." - debug.info(1, fmt_str.format(self.storage_nets)) + debug.warning(fmt_str.format(self.storage_nets)) return None def get_storage_net_offset(self): diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index e985e951..846161e5 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -576,7 +576,11 @@ class simulation(): """ Gets the signal name associated with the bitlines in the bank. """ - cell_mod = factory.create(module_type=OPTS.bitcell) + # FIXME: change to a solution that does not depend on the technology + if OPTS.tech_name == 'sky130': + cell_mod = factory.create(module_type=OPTS.bitcell, version="opt1") + else: + cell_mod = factory.create(module_type=OPTS.bitcell) cell_bl = cell_mod.get_bl_name(port) cell_br = cell_mod.get_br_name(port) diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index b890566a..d237bcc8 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -238,5 +238,4 @@ class replica_column(bitcell_base_array): for row, cell in enumerate(self.cell_inst): if row != self.replica_bit: self.graph_inst_exclude.add(cell) - - + From 1e486cd34429724affb665942031f2f071c3dcbe Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Jun 2021 18:41:39 -0700 Subject: [PATCH 15/36] Use local spacing rule --- compiler/modules/hierarchical_decoder.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 7eee7b69..24267719 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -508,7 +508,7 @@ class hierarchical_decoder(design.design): length=self.height) self.route_bus_to_decoder() self.route_predecodes_to_bus() - + def route_predecodes_to_bus(self): """ @@ -652,7 +652,7 @@ class hierarchical_decoder(design.design): offset=pin_pos, directions=("H", "H")) self.predecode_bus_rail_pos.append(rail_pos) - + def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset, predecode_type): """ Connect the routing rail to the given metal1 pin using a jog @@ -672,7 +672,7 @@ class hierarchical_decoder(design.design): #pin_pos = pin.center() #rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y) #self.add_path(self.output_layer, [pin_pos, rail_pos]) - + # create via for dimensions from_layer = self.output_layer to_layer = self.bus_layer @@ -692,7 +692,7 @@ class hierarchical_decoder(design.design): layer_stack=curr_stack, dimensions=[1, 1], directions=self.bus_directions) - overlapping_pin_space = drc["{0}_to_{0}".format(self.output_layer)] + overlapping_pin_space = getattr(self, "{}_space".format(self.output_layer)) total_buffer_space = (overlapping_pin_space + via.height) while(True): drc_error = 0 @@ -705,7 +705,7 @@ class hierarchical_decoder(design.design): else: y_offset += drc["grid"] rail_pos.y = y_offset - + if predecode_type == "pre2x4": right_pos = pin_pos elif predecode_type =="pre3x8": From d119a0e7ff52aa8c7675c8f79a73e70ebfe8e7f3 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Jun 2021 18:45:53 -0700 Subject: [PATCH 16/36] Use sky130 bitcell in simulation for BLs --- compiler/characterizer/simulation.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index e985e951..ff2275f6 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -482,7 +482,7 @@ class simulation(): debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.") debug.info(2, "s_en name = {}".format(self.sen_name)) - + column_addr = self.get_column_addr() bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port) port_pos = -1 - len(str(column_addr)) - len(str(port)) @@ -576,7 +576,11 @@ class simulation(): """ Gets the signal name associated with the bitlines in the bank. """ - cell_mod = factory.create(module_type=OPTS.bitcell) + # FIXME: change to a solution that does not depend on the technology + if OPTS.tech_name == 'sky130': + cell_mod = factory.create(module_type=OPTS.bitcell, version="opt1") + else: + cell_mod = factory.create(module_type=OPTS.bitcell) cell_bl = cell_mod.get_bl_name(port) cell_br = cell_mod.get_br_name(port) @@ -588,14 +592,14 @@ class simulation(): for i in range(len(bl_names)): bl_names[i] = bl_names[i].split(OPTS.hier_seperator)[-1] return bl_names[0], bl_names[1] - + def get_empty_measure_data_dict(self): """Make a dict of lists for each type of delay and power measurement to append results to""" measure_names = self.delay_meas_names + self.power_meas_names # Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. measure_data = [{mname: [] for mname in measure_names} for i in self.all_ports] - return measure_data + return measure_data def sum_delays(self, delays): """Adds the delays (delay_data objects) so the correct slew is maintained""" @@ -604,5 +608,3 @@ class simulation(): for i in range(1, len(delays)): delay+=delays[i] return delay - - From c7c319c11f77a0d5e3a929f3b5ae206d02df3b76 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Jun 2021 19:06:12 -0700 Subject: [PATCH 17/36] Use extra bitcell version tag only for single port in sky130 --- compiler/characterizer/simulation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index ff2275f6..be09ad89 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -577,7 +577,7 @@ class simulation(): Gets the signal name associated with the bitlines in the bank. """ # FIXME: change to a solution that does not depend on the technology - if OPTS.tech_name == 'sky130': + if OPTS.tech_name == "sky130" and self.total_ports == 1: cell_mod = factory.create(module_type=OPTS.bitcell, version="opt1") else: cell_mod = factory.create(module_type=OPTS.bitcell) From b7f1c8e8fc4b8f3328883187ba24c720ae65613f Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Jun 2021 19:07:56 -0700 Subject: [PATCH 18/36] Fix name for detecting single port --- compiler/characterizer/simulation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index be09ad89..9a8f00f8 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -577,7 +577,7 @@ class simulation(): Gets the signal name associated with the bitlines in the bank. """ # FIXME: change to a solution that does not depend on the technology - if OPTS.tech_name == "sky130" and self.total_ports == 1: + if OPTS.tech_name == "sky130" and len(self.all_ports) == 1: cell_mod = factory.create(module_type=OPTS.bitcell, version="opt1") else: cell_mod = factory.create(module_type=OPTS.bitcell) From afe09025470ddc7e06a7e0e9a72c5339aece9daf Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Jun 2021 19:13:50 -0700 Subject: [PATCH 19/36] Enable small short func tests --- ...nc_test.py => 50_riscv_1rw1r_func_test.py} | 7 +-- compiler/tests/50_riscv_1rw_func_test.py | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) rename compiler/tests/{50_riscv_func_test.py => 50_riscv_1rw1r_func_test.py} (93%) create mode 100755 compiler/tests/50_riscv_1rw_func_test.py diff --git a/compiler/tests/50_riscv_func_test.py b/compiler/tests/50_riscv_1rw1r_func_test.py similarity index 93% rename from compiler/tests/50_riscv_func_test.py rename to compiler/tests/50_riscv_1rw1r_func_test.py index 5bd55e96..c643621e 100755 --- a/compiler/tests/50_riscv_func_test.py +++ b/compiler/tests/50_riscv_1rw1r_func_test.py @@ -16,7 +16,7 @@ from sram_factory import factory import debug -@unittest.skip("SKIPPING 50_riscv_func_test") +# @unittest.skip("SKIPPING 50_riscv_func_test") class riscv_func_test(openram_test): def runTest(self): @@ -24,7 +24,6 @@ class riscv_func_test(openram_test): globals.init_openram(config_file) OPTS.analytical_delay = False OPTS.netlist_only = True - OPTS.local_array_size = 16 OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 @@ -38,7 +37,7 @@ class riscv_func_test(openram_test): from sram_config import sram_config c = sram_config(word_size=32, write_size=8, - num_words=256, + num_words=32, num_banks=1) c.words_per_row=1 c.recompute_sizes() @@ -49,7 +48,7 @@ class riscv_func_test(openram_test): c.num_banks)) s = factory.create(module_type="sram", sram_config=c) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - f = functional(s.s, corner=corner) + f = functional(s.s, corner=corner, cycles=50) (fail, error) = f.run() self.assertTrue(fail, error) diff --git a/compiler/tests/50_riscv_1rw_func_test.py b/compiler/tests/50_riscv_1rw_func_test.py new file mode 100755 index 00000000..00921ec4 --- /dev/null +++ b/compiler/tests/50_riscv_1rw_func_test.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +# @unittest.skip("SKIPPING 50_riscv_func_test") +class riscv_func_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + globals.setup_bitcell() + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional + from sram_config import sram_config + c = sram_config(word_size=32, + write_size=8, + num_words=32, + num_banks=1) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Functional test RISC-V memory" + "{} bit words, {} words, {} words per row, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, corner=corner, cycles=50) + (fail, error) = f.run() + self.assertTrue(fail, error) + + globals.end_openram() + +# instantiate a copy 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(testRunner=debugTestRunner()) From 1ce6b4d41af5c6b839f10207cdbab308a8efe80a Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 17 Jun 2021 03:21:01 -0700 Subject: [PATCH 20/36] fix freepdk45 --- compiler/base/custom_layer_properties.py | 7 +++-- compiler/modules/hierarchical_predecode.py | 29 ++++++++++++------- .../tests/06_hierarchical_decoder_test.py | 6 ++-- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/compiler/base/custom_layer_properties.py b/compiler/base/custom_layer_properties.py index eff24f82..8e20d031 100644 --- a/compiler/base/custom_layer_properties.py +++ b/compiler/base/custom_layer_properties.py @@ -45,7 +45,8 @@ class _hierarchical_predecode: bus_space_factor, input_layer, output_layer, - vertical_supply): + vertical_supply, + force_horizontal_input_contact): # hierarchical_predecode # bus_layer, bus_directions, bus_pitch, bus_space, input_layer, output_layer, output_layer_pitch # m2, pref, m2_pitch, m2_space, m1, m1, m1_pitch @@ -59,6 +60,7 @@ class _hierarchical_predecode: self.input_layer = input_layer self.output_layer = output_layer self.vertical_supply = vertical_supply + self.force_horizontal_input_contact = force_horizontal_input_contact class _column_mux_array: @@ -152,7 +154,8 @@ class layer_properties(): bus_space_factor=1, input_layer="m1", output_layer="m1", - vertical_supply=False) + vertical_supply=False, + force_horizontal_input_contact=False) self._column_mux_array = _column_mux_array(select_layer="m1", select_pitch="m2_pitch", diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 93c4b8d7..63086989 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -215,17 +215,26 @@ class hierarchical_predecode(design.design): in_pos = vector(self.input_rails[in_pin].cx(), y_offset) a_pos = vector(self.decode_rails[a_pin].cx(), y_offset) self.add_path(self.input_layer, [in_pos, a_pos]) - - self.add_via_stack_center(from_layer=self.input_layer, - to_layer=self.bus_layer, - offset=[self.input_rails[in_pin].cx(), y_offset], - directions= ("H", "H")) - - self.add_via_stack_center(from_layer=self.input_layer, - to_layer=self.bus_layer, - offset=[self.decode_rails[a_pin].cx(), y_offset], - directions=("H", "H")) + if(layer_props.hierarchical_predecode.force_horizontal_input_contact): + print("ping") + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, + offset=[self.input_rails[in_pin].cx(), y_offset], + directions= ("H", "H")) + + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, + offset=[self.decode_rails[a_pin].cx(), y_offset], + directions=("H", "H")) + else: + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, + offset=[self.input_rails[in_pin].cx(), y_offset]) + + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, + offset=[self.decode_rails[a_pin].cx(), y_offset]) def route_output_ands(self): """ Route all conections of the outputs and gates diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index 6cc17ee3..ae55ba3e 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -58,9 +58,9 @@ class hierarchical_decoder_test(openram_test): self.local_check(a) # Checks 3 x 4x16 and 4-input NAND decoder - debug.info(1, "Testing 4096 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=4096) - self.local_check(a) + #debug.info(1, "Testing 4096 row sample for hierarchical_decoder") + #a = factory.create(module_type="hierarchical_decoder", num_outputs=4096) + #self.local_check(a) globals.end_openram() From d9afe897701acf4efbaa2b372c5f18452eaeeccc Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 17 Jun 2021 03:23:46 -0700 Subject: [PATCH 21/36] remove print statement --- compiler/modules/hierarchical_predecode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 63086989..30908c4c 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -217,7 +217,6 @@ class hierarchical_predecode(design.design): self.add_path(self.input_layer, [in_pos, a_pos]) if(layer_props.hierarchical_predecode.force_horizontal_input_contact): - print("ping") self.add_via_stack_center(from_layer=self.input_layer, to_layer=self.bus_layer, offset=[self.input_rails[in_pin].cx(), y_offset], From 81d20ec2aab55b5a4ae06fccb873a6a08c7c795c Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 18 Jun 2021 07:23:41 -0700 Subject: [PATCH 22/36] Add spare cols to behavioral Verilog model --- compiler/base/verilog.py | 55 +++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index c2f9833a..6358103b 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -37,13 +37,13 @@ class verilog: self.gnd_name = spice["ground"] except KeyError: self.gnd_name = "gnd" - + self.vf.write("module {0}(\n".format(self.name)) self.vf.write("`ifdef USE_POWER_PINS\n") self.vf.write(" {},\n".format(self.vdd_name)) self.vf.write(" {},\n".format(self.gnd_name)) self.vf.write("`endif\n") - + for port in self.all_ports: if port in self.readwrite_ports: self.vf.write("// Port {0}: RW\n".format(port)) @@ -55,11 +55,15 @@ class verilog: self.vf.write(" clk{0},csb{0},web{0},".format(port)) if self.write_size: self.vf.write("wmask{},".format(port)) + if self.num_spare_cols > 0: + self.vf.write(" spare_wen{0},".format(port)) self.vf.write("addr{0},din{0},dout{0}".format(port)) elif port in self.write_ports: self.vf.write(" clk{0},csb{0},".format(port)) if self.write_size: self.vf.write("wmask{},".format(port)) + if self.num_spare_cols > 0: + self.vf.write(" spare_wen{0},".format(port)) self.vf.write("addr{0},din{0}".format(port)) elif port in self.read_ports: self.vf.write(" clk{0},csb{0},addr{0},dout{0}".format(port)) @@ -71,7 +75,7 @@ class verilog: if self.write_size: self.num_wmasks = int(math.ceil(self.word_size / self.write_size)) self.vf.write(" parameter NUM_WMASKS = {0} ;\n".format(self.num_wmasks)) - self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size)) + self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size + self.num_spare_cols)) self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(self.addr_size)) self.vf.write(" parameter RAM_DEPTH = 1 << ADDR_WIDTH;\n") self.vf.write(" // FIXME: This delay is arbitrary.\n") @@ -84,7 +88,7 @@ class verilog: self.vf.write(" inout {};\n".format(self.vdd_name)) self.vf.write(" inout {};\n".format(self.gnd_name)) self.vf.write("`endif\n") - + for port in self.all_ports: self.add_inputs_outputs(port) @@ -123,6 +127,10 @@ class verilog: if port in self.write_ports: if self.write_size: self.vf.write(" reg [NUM_WMASKS-1:0] wmask{0}_reg;\n".format(port)) + if self.num_spare_cols > 1: + self.vf.write(" reg [{1}:0] spare_wen{0}_reg;".format(port, self.num_spare_cols - 1)) + elif self.num_spare_cols == 1: + self.vf.write(" reg spare_wen{0}_reg;\n".format(port)) self.vf.write(" reg [ADDR_WIDTH-1:0] addr{0}_reg;\n".format(port)) if port in self.write_ports: self.vf.write(" reg [DATA_WIDTH-1:0] din{0}_reg;\n".format(port)) @@ -143,7 +151,9 @@ class verilog: if port in self.write_ports: if self.write_size: self.vf.write(" wmask{0}_reg = wmask{0};\n".format(port)) - self.vf.write(" addr{0}_reg = addr{0};\n".format(port)) + if self.num_spare_cols: + self.vf.write(" spare_wen{0}_reg = spare_wen{0};\n".format(port)) + self.vf.write(" addr{0}_reg = addr{0};\n".format(port)) if port in self.read_ports: self.add_write_read_checks(port) @@ -182,6 +192,11 @@ class verilog: self.vf.write(" input web{0}; // active low write control\n".format(port)) if self.write_size: self.vf.write(" input [NUM_WMASKS-1:0] wmask{0}; // write mask\n".format(port)) + if self.num_spare_cols > 1: + self.vf.write(" input [{1}:0] spare_wen{0}; // write mask\n".format(port, self.num_spare_cols-1)) + else: + self.vf.write(" input spare_wen{0}; // write mask\n".format(port)) + self.vf.write(" input [ADDR_WIDTH-1:0] addr{0};\n".format(port)) if port in self.write_ports: self.vf.write(" input [DATA_WIDTH-1:0] din{0};\n".format(port)) @@ -199,29 +214,29 @@ class verilog: self.vf.write(" always @ (negedge clk{0})\n".format(port)) self.vf.write(" begin : MEM_WRITE{0}\n".format(port)) if port in self.readwrite_ports: - if self.write_size: - self.vf.write(" if ( !csb{0}_reg && !web{0}_reg ) begin\n".format(port)) - else: - self.vf.write(" if ( !csb{0}_reg && !web{0}_reg )\n".format(port)) + self.vf.write(" if ( !csb{0}_reg && !web{0}_reg ) begin\n".format(port)) else: - if self.write_size: - self.vf.write(" if (!csb{0}_reg) begin\n".format(port)) - else: - self.vf.write(" if (!csb{0}_reg)\n".format(port)) + self.vf.write(" if (!csb{0}_reg) begin\n".format(port)) if self.write_size: - remainder_bits = self.word_size % self.write_size for mask in range(0, self.num_wmasks): lower = mask * self.write_size - if (remainder_bits and mask == self.num_wmasks - 1): - upper = lower + remainder_bits - 1 - else: - upper = lower + self.write_size - 1 + upper = lower + self.write_size - 1 self.vf.write(" if (wmask{0}_reg[{1}])\n".format(port, mask)) self.vf.write(" mem[addr{0}_reg][{1}:{2}] = din{0}_reg[{1}:{2}];\n".format(port, upper, lower)) - self.vf.write(" end\n") else: - self.vf.write(" mem[addr{0}_reg] = din{0}_reg;\n".format(port)) + upper = self.word_size - self.num_spare_cols - 1 + self.vf.write(" mem[addr{0}_reg][{1}:0] = din{0}_reg[{1}:0];\n".format(port, upper)) + + if self.num_spare_cols == 1: + self.vf.write(" if (spare_wen{0}_reg)\n".format(port)) + self.vf.write(" mem[addr{0}_reg][{1}] = din{0}_reg[{1}];\n".format(port, self.word_size + num)) + else: + for num in range(self.num_spare_cols): + self.vf.write(" if (spare_wen{0}_reg[{1}])\n".format(port, num)) + self.vf.write(" mem[addr{0}_reg][{1}] = din{0}_reg[{1}];\n".format(port, self.word_size + num)) + + self.vf.write(" end\n") self.vf.write(" end\n") def add_read_block(self, port): From 67877175b2af841c07438b51af0ff8b022a58d5c Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 18 Jun 2021 08:41:26 -0700 Subject: [PATCH 23/36] Fix error in no spare column verilog --- compiler/base/verilog.py | 12 ++++++------ compiler/tests/golden/sram_2_16_1_freepdk45.v | 5 +++-- compiler/tests/golden/sram_2_16_1_scn4m_subm.v | 5 +++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index 6358103b..3da3b9aa 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -56,14 +56,14 @@ class verilog: if self.write_size: self.vf.write("wmask{},".format(port)) if self.num_spare_cols > 0: - self.vf.write(" spare_wen{0},".format(port)) + self.vf.write("spare_wen{0},".format(port)) self.vf.write("addr{0},din{0},dout{0}".format(port)) elif port in self.write_ports: self.vf.write(" clk{0},csb{0},".format(port)) if self.write_size: self.vf.write("wmask{},".format(port)) if self.num_spare_cols > 0: - self.vf.write(" spare_wen{0},".format(port)) + self.vf.write("spare_wen{0},".format(port)) self.vf.write("addr{0},din{0}".format(port)) elif port in self.read_ports: self.vf.write(" clk{0},csb{0},addr{0},dout{0}".format(port)) @@ -192,10 +192,10 @@ class verilog: self.vf.write(" input web{0}; // active low write control\n".format(port)) if self.write_size: self.vf.write(" input [NUM_WMASKS-1:0] wmask{0}; // write mask\n".format(port)) - if self.num_spare_cols > 1: - self.vf.write(" input [{1}:0] spare_wen{0}; // write mask\n".format(port, self.num_spare_cols-1)) - else: - self.vf.write(" input spare_wen{0}; // write mask\n".format(port)) + if self.num_spare_cols == 1: + self.vf.write(" input spare_wen{0}; // spare mask\n".format(port)) + elif self.num_spare_cols > 1: + self.vf.write(" input [{1}:0] spare_wen{0}; // spare mask\n".format(port, self.num_spare_cols-1)) self.vf.write(" input [ADDR_WIDTH-1:0] addr{0};\n".format(port)) if port in self.write_ports: diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45.v b/compiler/tests/golden/sram_2_16_1_freepdk45.v index 4441b717..859d1cc6 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45.v +++ b/compiler/tests/golden/sram_2_16_1_freepdk45.v @@ -56,8 +56,9 @@ reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; // Write Operation : When web0 = 0, csb0 = 0 always @ (negedge clk0) begin : MEM_WRITE0 - if ( !csb0_reg && !web0_reg ) - mem[addr0_reg] = din0_reg; + if ( !csb0_reg && !web0_reg ) begin + mem[addr0_reg][1:0] = din0_reg[1:0]; + end end // Memory Read Block Port 0 diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm.v b/compiler/tests/golden/sram_2_16_1_scn4m_subm.v index fd77a66e..ce3714b2 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm.v +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm.v @@ -56,8 +56,9 @@ reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; // Write Operation : When web0 = 0, csb0 = 0 always @ (negedge clk0) begin : MEM_WRITE0 - if ( !csb0_reg && !web0_reg ) - mem[addr0_reg] = din0_reg; + if ( !csb0_reg && !web0_reg ) begin + mem[addr0_reg][1:0] = din0_reg[1:0]; + end end // Memory Read Block Port 0 From 12999893328683e044afa5b78d41e9f3d6547cfb Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 18 Jun 2021 08:43:21 -0700 Subject: [PATCH 24/36] Fix single spare_wen naming --- compiler/sram/sram_base.py | 39 +++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 01cf65b7..a6a885dc 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -84,8 +84,11 @@ class sram_base(design, verilog, lef): for port in self.write_ports: for bit in range(self.num_wmasks): self.add_pin("wmask{0}[{1}]".format(port, bit), "INPUT") - for bit in range(self.num_spare_cols): - self.add_pin("spare_wen{0}[{1}]".format(port, bit), "INPUT") + if self.num_spare_cols == 1: + self.add_pin("spare_wen{0}".format(port), "INPUT") + else: + for bit in range(self.num_spare_cols): + self.add_pin("spare_wen{0}[{1}]".format(port, bit), "INPUT") for port in self.read_ports: for bit in range(self.word_size + self.num_spare_cols): self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT") @@ -246,7 +249,7 @@ class sram_base(design, verilog, lef): for pin_name in ["vdd", "gnd"]: for inst in self.insts: self.copy_power_pins(inst, pin_name, self.ext_supply[pin_name]) - + if not OPTS.route_supplies: # Do not route the power supply (leave as must-connect pins) return @@ -282,11 +285,11 @@ class sram_base(design, verilog, lef): pin.ll(), pin.width(), pin.height()) - + elif OPTS.route_supplies and OPTS.supply_pin_type == "single": # Update these as we may have routed outside the region (perimeter pins) lowest_coord = self.find_lowest_coords() - + # Find the lowest leftest pin for vdd and gnd for pin_name in ["vdd", "gnd"]: # Copy the pin shape(s) to rectangles @@ -337,7 +340,7 @@ class sram_base(design, verilog, lef): pins_to_route.append("{0}{1}".format(signal, port)) else: pins_to_route.append("{0}{1}".format(signal, port)) - + if port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): pins_to_route.append("din{0}[{1}]".format(port, bit)) @@ -358,8 +361,11 @@ class sram_base(design, verilog, lef): pins_to_route.append("wmask{0}[{1}]".format(port, bit)) if port in self.write_ports: - for bit in range(self.num_spare_cols): - pins_to_route.append("spare_wen{0}[{1}]".format(port, bit)) + if self.num_spare_cols == 1: + pins_to_route.append("spare_wen{0}".format(port)) + else: + for bit in range(self.num_spare_cols): + pins_to_route.append("spare_wen{0}[{1}]".format(port, bit)) from signal_escape_router import signal_escape_router as router rtr=router(layers=self.m3_stack, @@ -562,8 +568,11 @@ class sram_base(design, verilog, lef): temp.append("w_en{0}".format(port)) for bit in range(self.num_wmasks): temp.append("bank_wmask{}[{}]".format(port, bit)) - for bit in range(self.num_spare_cols): - temp.append("bank_spare_wen{0}[{1}]".format(port, bit)) + if self.num_spare_cols == 1: + temp.append("bank_spare_wen{0}".format(port)) + else: + for bit in range(self.num_spare_cols): + temp.append("bank_spare_wen{0}_{1}".format(port, bit)) for port in self.all_ports: temp.append("wl_en{0}".format(port)) temp.extend(self.ext_supplies) @@ -695,9 +704,13 @@ class sram_base(design, verilog, lef): # inputs, outputs/output/bar inputs = [] outputs = [] - for bit in range(self.num_spare_cols): - inputs.append("spare_wen{}[{}]".format(port, bit)) - outputs.append("bank_spare_wen{}[{}]".format(port, bit)) + if self.num_spare_cols == 1: + inputs.append("spare_wen{}".format(port)) + outputs.append("bank_spare_wen{}".format(port)) + else: + for bit in range(self.num_spare_cols): + inputs.append("spare_wen{}[{}]".format(port, bit)) + outputs.append("bank_spare_wen{}_{}".format(port, bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) From 693a81fa8d3f6a94fea025051ba3359d08c760f3 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 18 Jun 2021 10:44:35 -0700 Subject: [PATCH 25/36] Fix spare_wen IO pin names --- compiler/sram/sram_1bank.py | 36 +++++++++++++++++++++--------------- compiler/sram/sram_base.py | 2 +- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 030b0c33..47514b16 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -102,7 +102,7 @@ class sram_1bank(sram_base): # Place with an initial wide channel (from above) self.place_dffs() - + # Route the channel and set to the new data bus size # We need to temporarily add some pins for the x offsets # but we'll remove them so that they have the right y @@ -110,7 +110,7 @@ class sram_1bank(sram_base): self.add_layout_pins(add_vias=False) self.route_dffs(add_routes=False) self.remove_layout_pins() - + # Re-place with the new channel size self.place_dffs() @@ -270,7 +270,7 @@ class sram_1bank(sram_base): signal, signal + "{}".format(port), start_layer=pin_layer) - + if port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): self.add_io_pin(self.data_dff_insts[port], @@ -303,15 +303,21 @@ class sram_1bank(sram_base): self.add_io_pin(self.wmask_dff_insts[port], "din_{}".format(bit), "wmask{0}[{1}]".format(port, bit), - start_layer=pin_layer) + start_layer=pin_layer) if port in self.write_ports: - for bit in range(self.num_spare_cols): + if self.num_spare_cols == 1: self.add_io_pin(self.spare_wen_dff_insts[port], - "din_{}".format(bit), - "spare_wen{0}[{1}]".format(port, bit), - start_layer=pin_layer) - + "din_{}".format(0), + "spare_wen{0}".format(port), + start_layer=pin_layer) + else: + for bit in range(self.num_spare_cols): + self.add_io_pin(self.spare_wen_dff_insts[port], + "din_{}".format(bit), + "spare_wen{0}[{1}]".format(port, bit), + start_layer=pin_layer) + def route_layout(self): """ Route a single bank SRAM """ @@ -351,11 +357,11 @@ class sram_1bank(sram_base): big_margin=big_margin, little_margin=little_margin) self.route_escape_pins(bbox) - + # Route the supplies first since the MST is not blockage aware # and signals can route to anywhere on sides (it is flexible) self.route_supplies(pre_bbox) - + def route_dffs(self, add_routes=True): for port in self.all_ports: @@ -371,7 +377,7 @@ class sram_1bank(sram_base): self.route_data_dffs(port, add_routes) def route_col_addr_dffs(self, port): - + route_map = [] # column mux dff is routed on it's own since it is to the far end @@ -412,10 +418,10 @@ class sram_1bank(sram_base): self.add_inst(cr.name, cr) self.connect_inst([]) # self.add_flat_inst(cr.name, cr) - + def route_data_dffs(self, port, add_routes): route_map = [] - + # wmask dff if self.num_wmasks > 0 and port in self.write_ports: dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)] @@ -450,7 +456,7 @@ class sram_1bank(sram_base): y_bottom = min(0, self.control_logic_insts[port].get_pin("s_en").by()) else: y_bottom = 0 - + y_offset = y_bottom - self.data_bus_size[port] + 2 * self.m3_pitch offset = vector(self.control_logic_insts[port].rx() + self.dff.width, y_offset) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index a6a885dc..553cf3a6 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -709,7 +709,7 @@ class sram_base(design, verilog, lef): outputs.append("bank_spare_wen{}".format(port)) else: for bit in range(self.num_spare_cols): - inputs.append("spare_wen{}[{}]".format(port, bit)) + inputs.append("spare_wen{}_{}]".format(port, bit)) outputs.append("bank_spare_wen{}_{}".format(port, bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) From 8ceece2af6739ed890a450c37aa4d0903332dcfe Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 18 Jun 2021 14:21:02 -0700 Subject: [PATCH 26/36] check for valid dimensions instead of recalcuating --- compiler/modules/bank.py | 12 +++--------- compiler/modules/port_data.py | 21 +++++++-------------- compiler/sram/sram_config.py | 11 ++++++++++- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 99ad8350..0e8b833f 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -392,14 +392,12 @@ class bank(design.design): cols=self.num_cols + self.num_spare_cols, rows=self.num_rows) self.add_mod(self.bitcell_array) - if self.num_spare_cols == 0: - self.num_spare_cols = (self.bitcell_array.column_size % (self.word_size *self.words_per_row)) self.port_address = [] for port in self.all_ports: self.port_address.append(factory.create(module_type="port_address", - cols=self.bitcell_array.column_size, - rows=self.bitcell_array.row_size, + cols=self.num_cols + self.num_spare_cols, + rows=self.num_rows, port=port)) self.add_mod(self.port_address[port]) @@ -408,10 +406,6 @@ class bank(design.design): for port in self.all_ports: temp_pre = factory.create(module_type="port_data", sram_config=self.sram_config, - dimension_override=True, - cols=self.bitcell_array.column_size - self.num_spare_cols, - rows=self.bitcell_array.row_size, - num_spare_cols=self.num_spare_cols, port=port, bit_offsets=self.bit_offsets) self.port_data.append(temp_pre) @@ -500,7 +494,7 @@ class bank(design.design): mod=self.port_address[port]) temp = [] - for bit in range(ceil(log(self.bitcell_array.row_size, 2))): + for bit in range(self.row_addr_size): temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size)) temp.append("wl_en{}".format(port)) wordline_names = self.bitcell_array.get_wordline_names(port) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 3fbb8696..fa0d5763 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -21,31 +21,24 @@ class port_data(design.design): Port 0 always has the RBL on the left while port 1 is on the right. """ - def __init__(self, sram_config, port, num_spare_cols=None, bit_offsets=None, name="", rows=None, cols=None, dimension_override=False): - sram_config.set_local_config(self) - if dimension_override: - self.num_rows = rows - self.num_cols = cols - self.word_size = sram_config.word_size + def __init__(self, sram_config, port, num_spare_cols=None, bit_offsets=None, name="",): + sram_config.set_local_config(self) self.port = port if self.write_size is not None: self.num_wmasks = int(math.ceil(self.word_size / self.write_size)) else: self.num_wmasks = 0 - - if num_spare_cols: - self.num_spare_cols = num_spare_cols - elif self.num_spare_cols is None: + + if num_spare_cols is not None: + self.num_spare_cols = num_spare_cols + self.num_spare_cols + if self.num_spare_cols is None: self.num_spare_cols = 0 - if not bit_offsets: bitcell = factory.create(module_type=OPTS.bitcell) if(cell_properties.use_strap == True and OPTS.num_ports == 1): strap = factory.create(module_type=cell_properties.strap_module, version=cell_properties.strap_version) precharge_width = bitcell.width + strap.width - else: - precharge_width = bitcell.width self.bit_offsets = [] for i in range(self.num_cols + self.num_spare_cols): self.bit_offsets.append(i * precharge_width) @@ -855,4 +848,4 @@ class port_data(design.design): def graph_exclude_precharge(self): """Precharge adds a loop between bitlines, can be excluded to reduce complexity""" if self.precharge_array_inst: - self.graph_inst_exclude.add(self.precharge_array_inst) + self.graph_inst_exclude.add(self.precharge_array_inst) \ No newline at end of file diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index b7e3cad4..640b2a8d 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -9,6 +9,8 @@ import debug from math import log, sqrt, ceil from globals import OPTS from sram_factory import factory +from tech import array_row_multiple +from tech import array_col_multiple class sram_config: @@ -46,7 +48,7 @@ class sram_config: self.num_words_per_bank = self.num_words / self.num_banks self.num_bits_per_bank = self.word_size * self.num_words_per_bank - + # If this was hard coded, don't dynamically compute it! if not self.words_per_row: # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry) @@ -96,6 +98,13 @@ class sram_config: + " Col addr size: {}".format(self.col_addr_size) + " Bank addr size: {}".format(self.bank_addr_size)) + num_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_w_ports + if ((self.num_cols + num_ports + self.num_spare_cols) % array_col_multiple != 0): + debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.num_cols + num_ports + self.num_spare_cols, array_col_multiple), -1) + + if ((self.num_rows + num_ports) % array_row_multiple != 0): + debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.num_rows + num_ports, array_row_multiple), -1) + def estimate_words_per_row(self, tentative_num_cols, word_size): """ This provides a heuristic rounded estimate for the number of words From 8346ad736e6c68b20517b9ef73201bae4a39c1b0 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 18 Jun 2021 14:36:15 -0700 Subject: [PATCH 27/36] add dimension contraints to other tech files --- technology/freepdk45/tech/tech.py | 3 +++ technology/scn3me_subm/tech/tech.py | 2 ++ technology/scn4m_subm/tech/tech.py | 3 +++ 3 files changed, 8 insertions(+) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index f5decd3c..46dc67fd 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -465,3 +465,6 @@ lvs_name = "calibre" pex_name = "calibre" blackbox_bitcell = False + +array_row_multiple = 1 +array_col_multiple = 1 \ No newline at end of file diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index 018a15da..39ca0cfc 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -305,3 +305,5 @@ pex_name = "magic" ################################################### ##END Technology Tool Preferences ################################################### +array_row_multiple = 1 +array_col_multiple = 1 \ No newline at end of file diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index dc6cd866..e5f50bd8 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -412,3 +412,6 @@ lvs_name = "netgen" pex_name = "magic" blackbox_bitcell = False + +array_row_multiple = 1 +array_col_multiple = 1 \ No newline at end of file From 0008df0204681be1297aafc83cfa9c8fdfafc078 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 18 Jun 2021 15:24:24 -0700 Subject: [PATCH 28/36] catch where strap size is zero --- compiler/modules/port_data.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index fa0d5763..762e788e 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -39,6 +39,8 @@ class port_data(design.design): if(cell_properties.use_strap == True and OPTS.num_ports == 1): strap = factory.create(module_type=cell_properties.strap_module, version=cell_properties.strap_version) precharge_width = bitcell.width + strap.width + else: + precharge_width = bitcell.width self.bit_offsets = [] for i in range(self.num_cols + self.num_spare_cols): self.bit_offsets.append(i * precharge_width) From 46889884344a97eb753957fb75392f1e65c28877 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 18 Jun 2021 17:46:39 -0700 Subject: [PATCH 29/36] only check dimensions on single port --- compiler/sram/sram_config.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index 640b2a8d..a5948cb2 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -99,12 +99,13 @@ class sram_config: + " Bank addr size: {}".format(self.bank_addr_size)) num_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_w_ports - if ((self.num_cols + num_ports + self.num_spare_cols) % array_col_multiple != 0): - debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.num_cols + num_ports + self.num_spare_cols, array_col_multiple), -1) + if num_ports == 1: + if ((self.num_cols + num_ports + self.num_spare_cols) % array_col_multiple != 0): + debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.num_cols + num_ports + self.num_spare_cols, array_col_multiple), -1) - if ((self.num_rows + num_ports) % array_row_multiple != 0): - debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.num_rows + num_ports, array_row_multiple), -1) - + if ((self.num_rows + num_ports) % array_row_multiple != 0): + debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.num_rows + num_ports, array_row_multiple), -1) + def estimate_words_per_row(self, tentative_num_cols, word_size): """ This provides a heuristic rounded estimate for the number of words From 2dbe928c090fac2578300596731e362e67afe8c1 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 18 Jun 2021 18:08:57 -0700 Subject: [PATCH 30/36] fix typo --- compiler/sram/sram_config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index a5948cb2..e3b33359 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -99,7 +99,8 @@ class sram_config: + " Bank addr size: {}".format(self.bank_addr_size)) num_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_w_ports - if num_ports == 1: + print(num_ports) + if num_ports != 1: if ((self.num_cols + num_ports + self.num_spare_cols) % array_col_multiple != 0): debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.num_cols + num_ports + self.num_spare_cols, array_col_multiple), -1) From 56dc83de476ec7129b22304e44f8038646e90653 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 18 Jun 2021 18:10:12 -0700 Subject: [PATCH 31/36] fix typo --- compiler/sram/sram_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index e3b33359..35226932 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -98,9 +98,9 @@ class sram_config: + " Col addr size: {}".format(self.col_addr_size) + " Bank addr size: {}".format(self.bank_addr_size)) - num_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_w_ports + num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports print(num_ports) - if num_ports != 1: + if num_ports == 1: if ((self.num_cols + num_ports + self.num_spare_cols) % array_col_multiple != 0): debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.num_cols + num_ports + self.num_spare_cols, array_col_multiple), -1) From af3102750490eecc691b58bc853bcbd0422f4714 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 21 Jun 2021 13:13:53 -0700 Subject: [PATCH 32/36] Fix error in 1 spare column Verilog --- compiler/base/verilog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index 3da3b9aa..205baeb7 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -230,7 +230,7 @@ class verilog: if self.num_spare_cols == 1: self.vf.write(" if (spare_wen{0}_reg)\n".format(port)) - self.vf.write(" mem[addr{0}_reg][{1}] = din{0}_reg[{1}];\n".format(port, self.word_size + num)) + self.vf.write(" mem[addr{0}_reg][{1}] = din{0}_reg[{1}];\n".format(port, self.word_size)) else: for num in range(self.num_spare_cols): self.vf.write(" if (spare_wen{0}_reg[{1}])\n".format(port, num)) From d53bc98ff552c4e824ee6c76c771103699014d4a Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 21 Jun 2021 13:14:08 -0700 Subject: [PATCH 33/36] Exit with error when spice models not found. Use ngspice if no simulator defined. --- compiler/characterizer/stimuli.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 384a9f4c..b247a8fb 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -22,7 +22,7 @@ from globals import OPTS class stimuli(): """ Class for providing stimuli functions """ - def __init__(self, stim_file, corner): + def __init__(self, stim_file, corner): self.vdd_name = "vdd" self.gnd_name = "gnd" self.pmos_name = tech.spice["pmos"] @@ -169,7 +169,7 @@ class stimuli(): def gen_constant(self, sig_name, v_val): """ Generates a constant signal with reference voltage and the voltage value """ self.sf.write("V{0} {0} 0 DC {1}\n".format(sig_name, v_val)) - + def get_voltage(self, value): if value == "0" or value == 0: return 0 @@ -301,21 +301,25 @@ class stimuli(): for item in self.device_libraries: if OPTS.spice_name: item[0] = item[0].replace("SIMULATOR", OPTS.spice_name.lower()) + else: + item[0] = item[0].replace("SIMULATOR", "ngspice") if os.path.isfile(item[0]): self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1])) else: - debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0])) + debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0]), -1) includes = self.device_models + [circuit] for item in list(includes): if OPTS.spice_name: item = item.replace("SIMULATOR", OPTS.spice_name.lower()) + else: + item = item.replace("SIMULATOR", "ngspice") self.sf.write(".include \"{0}\"\n".format(item)) def add_comment(self, msg): self.sf.write(msg + "\n") - + def write_supply(self): """ Writes supply voltage statements """ gnd_node_name = "0" @@ -407,5 +411,3 @@ class stimuli(): end_time = datetime.datetime.now() delta_time = round((end_time - start_time).total_seconds(), 1) debug.info(2, "*** Spice: {} seconds".format(delta_time)) - - From f3f19aeeeb93e5e87f75056da9ba4ca539b140a3 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 21 Jun 2021 15:16:36 -0700 Subject: [PATCH 34/36] Remove print statement --- compiler/sram/sram_config.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index 35226932..0916d0f4 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -48,7 +48,7 @@ class sram_config: self.num_words_per_bank = self.num_words / self.num_banks self.num_bits_per_bank = self.word_size * self.num_words_per_bank - + # If this was hard coded, don't dynamically compute it! if not self.words_per_row: # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry) @@ -65,11 +65,11 @@ class sram_config: self.recompute_sizes() - # Set word_per_row in OPTS + # Set word_per_row in OPTS OPTS.words_per_row = self.words_per_row debug.info(1, "Set SRAM Words Per Row={}".format(OPTS.words_per_row)) - + def recompute_sizes(self): """ Calculate the auxiliary values assuming fixed number of words per row. @@ -99,14 +99,13 @@ class sram_config: + " Bank addr size: {}".format(self.bank_addr_size)) num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports - print(num_ports) if num_ports == 1: if ((self.num_cols + num_ports + self.num_spare_cols) % array_col_multiple != 0): debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.num_cols + num_ports + self.num_spare_cols, array_col_multiple), -1) if ((self.num_rows + num_ports) % array_row_multiple != 0): debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.num_rows + num_ports, array_row_multiple), -1) - + def estimate_words_per_row(self, tentative_num_cols, word_size): """ This provides a heuristic rounded estimate for the number of words From 2760beae341a736ac26ecd30b2308f3357c71836 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Mon, 21 Jun 2021 15:22:31 -0700 Subject: [PATCH 35/36] swap sky130 replica bitcell array power bias routing --- compiler/modules/bank.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 0e8b833f..8aa46b18 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -620,7 +620,7 @@ class bank(design.design): self.copy_power_pins(inst, "gnd", add_vias=False) if 'vpb' in self.bitcell_array_inst.mod.pins and 'vnb' in self.bitcell_array_inst.mod.pins: - for pin_name, supply_name in zip(['vpb','vnb'],['vdd','gnd']): + for pin_name, supply_name in zip(['vpb','vnb'],['gnd','vdd']): self.copy_power_pins(self.bitcell_array_inst, pin_name, new_name=supply_name) # If we use the pinvbuf as the decoder, we need to add power pins. From bb1ac1a38ee508fe240edd70bcde990f40a36925 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 21 Jun 2021 15:23:08 -0700 Subject: [PATCH 36/36] Fix incorrect bus indexing of spare_wen. Convert internal signals to not use braces. --- compiler/sram/sram_base.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 553cf3a6..0b8eac44 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -15,7 +15,7 @@ from design import design from verilog import verilog from lef import lef from sram_factory import factory -from tech import spice, layer +from tech import spice class sram_base(design, verilog, lef): @@ -553,13 +553,13 @@ class sram_base(design, verilog, lef): temp.append("rbl_bl{0}".format(port)) for port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): - temp.append("bank_din{0}[{1}]".format(port, bit)) + temp.append("bank_din{0}_{1}".format(port, bit)) for port in self.all_ports: for bit in range(self.bank_addr_size): - temp.append("a{0}[{1}]".format(port, bit)) + temp.append("a{0}_{1}".format(port, bit)) if(self.num_banks > 1): for port in self.all_ports: - temp.append("bank_sel{0}[{1}]".format(port, bank_num)) + temp.append("bank_sel{0}_{1}".format(port, bank_num)) for port in self.read_ports: temp.append("s_en{0}".format(port)) for port in self.all_ports: @@ -567,12 +567,9 @@ class sram_base(design, verilog, lef): for port in self.write_ports: temp.append("w_en{0}".format(port)) for bit in range(self.num_wmasks): - temp.append("bank_wmask{}[{}]".format(port, bit)) - if self.num_spare_cols == 1: - temp.append("bank_spare_wen{0}".format(port)) - else: - for bit in range(self.num_spare_cols): - temp.append("bank_spare_wen{0}_{1}".format(port, bit)) + temp.append("bank_wmask{0}_{1}".format(port, bit)) + for bit in range(self.num_spare_cols): + temp.append("bank_spare_wen{0}_{1}".format(port, bit)) for port in self.all_ports: temp.append("wl_en{0}".format(port)) temp.extend(self.ext_supplies) @@ -622,7 +619,7 @@ class sram_base(design, verilog, lef): outputs = [] for bit in range(self.row_addr_size): inputs.append("addr{}[{}]".format(port, bit + self.col_addr_size)) - outputs.append("a{}[{}]".format(port, bit + self.col_addr_size)) + outputs.append("a{}_{}".format(port, bit + self.col_addr_size)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) @@ -640,7 +637,7 @@ class sram_base(design, verilog, lef): outputs = [] for bit in range(self.col_addr_size): inputs.append("addr{}[{}]".format(port, bit)) - outputs.append("a{}[{}]".format(port, bit)) + outputs.append("a{}_{}".format(port, bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) @@ -662,7 +659,7 @@ class sram_base(design, verilog, lef): outputs = [] for bit in range(self.word_size + self.num_spare_cols): inputs.append("din{}[{}]".format(port, bit)) - outputs.append("bank_din{}[{}]".format(port, bit)) + outputs.append("bank_din{}_{}".format(port, bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) @@ -684,7 +681,7 @@ class sram_base(design, verilog, lef): outputs = [] for bit in range(self.num_wmasks): inputs.append("wmask{}[{}]".format(port, bit)) - outputs.append("bank_wmask{}[{}]".format(port, bit)) + outputs.append("bank_wmask{}_{}".format(port, bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) @@ -709,7 +706,7 @@ class sram_base(design, verilog, lef): outputs.append("bank_spare_wen{}".format(port)) else: for bit in range(self.num_spare_cols): - inputs.append("spare_wen{}_{}]".format(port, bit)) + inputs.append("spare_wen{}[{}]".format(port, bit)) outputs.append("bank_spare_wen{}_{}".format(port, bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)