diff --git a/.coveragerc b/.coveragerc index 5a8c6f66..0df8ae34 100644 --- a/.coveragerc +++ b/.coveragerc @@ -4,11 +4,26 @@ omit = */.local/* # omit everything in /usr /usr/* + # ignore the unit tests themselves + */tests/* + # ignore the debug utilities + debug.py [paths] source = + ../.. /home/gitlab-runner/builds/2fd64746/0 /home/gitlab-runner/builds/2fd64746/1 /home/gitlab-runner/builds/2fd64746/2 /home/gitlab-runner/builds/2fd64746/3 /home/gitlab-runner/builds/2fd64746/4 /home/gitlab-runner/builds/2fd64746/5 +[report] +exclude_lines = + pragma: no cover + def __repr__ + except Exception + raise AssertionError + raise NotImplementedError + if 0: + if __name__ == "__main__": + if not OPTS.is_unit_test diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6eceeaaf..dacf53e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,6 +7,26 @@ list at openram-dev-group@ucsc.edu. We are happy to give insights into the best way to implement a change to ensure your contribution will be accepted and help other OpenRAM users. +# Directory Structure + +* compiler - openram compiler itself (pointed to by OPENRAM_HOME) + * compiler/base - base data structure modules + * compiler/pgates - parameterized cells (e.g. logic gates) + * compiler/bitcells - various bitcell styles + * compiler/modules - high-level modules (e.g. decoders, etc.) + * compiler/verify - DRC and LVS verification wrappers + * compiler/characterizer - timing characterization code + * compiler/gdsMill - GDSII reader/writer + * compiler/router - router for signals and power supplies + * compiler/tests - unit tests +* technology - openram technology directory (pointed to by OPENRAM_TECH) + * technology/freepdk45 - example configuration library for [FreePDK45 technology node + * technology/scn4m_subm - example configuration library [SCMOS] technology node + * technology/scn3me_subm - unsupported configuration (not enough metal layers) + * technology/setup_scripts - setup scripts to customize your PDKs and OpenRAM technologies +* docs - LaTeX manual (outdated) +* lib - IP library of pregenerated memories + # Code Style Our code may not be the best and we acknowledge that. We welcome @@ -20,7 +40,7 @@ other OpenRAM features. Please see the README.md file on how to run the unit tests. Unit tests should work in all technologies. We will run the tests on your contributions before they will be accepted. -# Internally Development +# Internal Development For internal development, follow all of the following steps EXCEPT do not fork your own copy. Instead, create a branch in our private repository @@ -32,21 +52,21 @@ All unit tests should pass first. 1. One time, create a GitHub account at http://github.com 2. Create a fork of the OpenRAM project on the github web page: - https://github.com/mguthaus/OpenRAM + https://github.com/vlsida/openram It is on the upper right and says "Fork": This will make your own OpenRAM repository on GitHub in your account. 3. Clone your repository (or use an existing cloned copy if you've already done this once): ``` - git clone https://github.com//OpenRAM.git - cd OpenRAM + git clone https://github.com//oepnram.git + cd openram ``` 4. Set up a new upstream that points to MY OpenRAM repository that you forked (only first time): ``` - git remote add upstream https://github.com/mguthaus/OpenRAM.git + git remote add upstream https://github.com/vlsida/openram.git ``` You now have two remotes for this project: * origin which points to your GitHub fork of the project. You can read diff --git a/HINTS.md b/HINTS.md new file mode 100644 index 00000000..b12704e5 --- /dev/null +++ b/HINTS.md @@ -0,0 +1,110 @@ +# Debugging + +When OpenRAM runs, it puts files in a temporary directory that is +shown in the banner at the top. Like: +``` + /tmp/openram_mrg_18128_temp/ +``` +This is where simulations and DRC/LVS get run so there is no network +traffic. The directory name is unique for each person and run of +OpenRAM to not clobber any files and allow simultaneous runs. If it +passes, the files are deleted. If it fails, you will see these files: ++ temp.gds is the layout (.mag files too if using SCMOS) ++ temp.sp is the netlist ++ test1.drc.err is the std err output of the DRC command ++ test1.drc.out is the standard output of the DRC command ++ test1.drc.results is the DRC results file ++ test1.lvs.err is the std err output of the LVS command ++ test1.lvs.out is the standard output of the LVS command ++ test1.lvs.results is the DRC results file + +Depending on your DRC/LVS tools, there will also be: ++ \_calibreDRC.rul\_ is the DRC rule file (Calibre) ++ dc_runset is the command file (Calibre) ++ extracted.sp (Calibre) ++ run_lvs.sh is a Netgen script for LVS (Netgen) ++ run_drc.sh is a Magic script for DRC (Magic) ++ .spice (Magic) + +If DRC/LVS fails, the first thing is to check if it ran in the .out and +.err file. This shows the standard output and error output from +running DRC/LVS. If there is a setup problem it will be shown here. + +If DRC/LVS runs, but doesn't pass, you then should look at the .results +file. If the DRC fails, it will typically show you the command that was used +to run Calibre or Magic+Netgen. + +To debug, you will need a layout viewer. I prefer to use Glade +on my Mac, but you can also use Calibre, Magic, etc. + +1. Calibre + + Start the Calibre DESIGNrev viewer in the temp directory and load your GDS file: +``` + calibredrv temp.gds +``` + Select Verification->Start RVE and select the results database file in + the new form (e.g., test1.drc.db). This will start the RVE (results + viewer). Scroll through the check pane and find the DRC check with an + error. Select it and it will open some numbers to the right. Double + click on any of the errors in the result browser. These will be + labelled as numbers "1 2 3 4" for example will be 4 DRC errors. + + In the viewer ">" opens the layout down a level. + +2. Glade + + You can view errors in Glade as well. I like this because it is on my laptop. + You can get it from: http://www.peardrop.co.uk/glade/ + + To remote display over X windows, you need to disable OpenGL acceleration or use vnc + or something. You can disable by adding this to your .bashrc in bash: +``` + export GLADE_USE_OPENGL=no +``` + or in .cshrc/.tcshrc in csh/tcsh: +``` + setenv GLADE_USE_OPENGAL no +``` + To use this with the FreePDK45 or SCMOS layer views you should use the + tech files. Then create a .glade.py file in your user directory with + these commands to load the technology layers: +``` +ui().importCds("default", +"/Users/mrg/techfiles/freepdk45/display.drf", +"/Users/mrg/techfiles/freepdk45/FreePDK45.tf", 1000, 1, +"/Users/mrg/techfiles/freepdk45/layers.map") +``` + Obviously, edit the paths to point to your directory. To switch + between processes, you have to change the importCds command (or you + can manually run the command each time you start glade). + + To load the errors, you simply do Verify->Import Calibre Errors select + the .results file from Calibre. + +3. Magic + + Magic is only supported in SCMOS. You will need to install the MOSIS SCMOS rules + and Magic from: http://opencircuitdesign.com/ + + When running DRC or extraction, OpenRAM will load the GDS file, save + the .ext/.mag files, and export an extracted netlist (.spice). + +4. It is possible to use other viewers as well, such as: + * LayoutEditor http://www.layouteditor.net/ + + +# Example to output/input .gds layout files from/to Cadence + +1. To create your component layouts, you should stream them to + individual gds files using our provided layermap and flatten + cells. For example, +``` + strmout -layerMap layers.map -library sram -topCell $i -view layout -flattenVias -flattenPcells -strmFile ../gds_lib/$i.gds +``` +2. To stream a layout back into Cadence, do this: +``` + strmin -layerMap layers.map -attachTechFileOfLib NCSU\_TechLib\_FreePDK45 -library sram_4_32 -strmFile sram_4_32.gds +``` + When you import a gds file, make sure to attach the correct tech lib + or you will get incorrect layers in the resulting library. diff --git a/README.md b/README.md index 44650183..5b875971 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ Dev: An open-source static random access memory (SRAM) compiler. # What is OpenRAM? - OpenRAM is an open-source Python framework to create the layout, @@ -31,7 +30,6 @@ The OpenRAM compiler has very few dependencies: + [Ngspice] 26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later) + Python 3.5 or higher + Python numpy (pip3 install numpy to install) -+ flask_table (pip3 install flask to install) If you want to perform DRC and LVS, you will need either: + Calibre (for [FreePDK45]) @@ -98,6 +96,12 @@ output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) # Disable analytical models for full characterization (WARNING: slow!) # analytical_delay = False + +# To force this to use magic and netgen for DRC/LVS/PEX +# Could be calibre for FreePDK45 +drc_name = "magic" +lvs_name = "netgen" +pex_name = "magic" ``` You can then run OpenRAM by executing: diff --git a/compiler/Makefile b/compiler/Makefile index af3eb2c0..a547b6ed 100644 --- a/compiler/Makefile +++ b/compiler/Makefile @@ -1,93 +1,45 @@ +TECH = scn4m_subm CUR_DIR = $(shell pwd) TEST_DIR = ${CUR_DIR}/tests -MAKEFLAGS += -j 2 +MAKEFLAGS += -j 1 # Library test -LIBRARY_TESTS = \ -01_library_drc_test.py \ -02_library_lvs_test.py +LIBRARY_TESTS = $(shell find ${TEST_DIR} -name 0[1-2]*_test.py) # Technology and DRC tests (along with ptx) -TECH_TESTS = \ -03_contact_test.py \ -03_ptx_1finger_pmos_test.py \ -03_ptx_4finger_nmos_test.py \ -03_path_test.py \ -03_ptx_3finger_nmos_test.py \ -03_ptx_4finger_pmos_test.py \ -03_ptx_1finger_nmos_test.py \ -03_ptx_3finger_pmos_test.py \ -03_wire_test.py +TECH_TESTS = $(shell find ${TEST_DIR} -name 03*_test.py) # Parameterized cells -PCELLS_TESTS = \ -04_pinv_1x_test.py \ -04_pinv_1x_beta_test.py \ -04_pinv_2x_test.py \ -04_pinv_10x_test.py \ -04_pnand2_test.py \ -04_pnor2_test.py \ -04_pnand3_test.py\ -04_wordline_driver_test.py \ -04_precharge_test.py +CELL_TESTS = $(shell find ${TEST_DIR} -name 04*_test.py) # Dynamically generated modules and arrays -MODULE_TESTS = \ -05_bitcell_array_test.py \ -06_hierarchical_decoder_test.py \ -06_hierarchical_predecode2x4_test.py \ -06_hierarchical_predecode3x8_test.py \ -07_single_level_column_mux_array_test.py \ -08_precharge_array_test.py \ -09_sense_amp_array_test.py \ -10_write_driver_array_test.py \ -11_ms_flop_array_test.py \ -12_tri_gate_array_test.py \ -13_delay_chain_test.py \ -14_replica_bitline_test.py \ -16_control_logic_test.py +MODULE_TESTS = $(shell find ${TEST_DIR} -name 0[5-9]*_test.py)\ +$(shell find ${TEST_DIR} -name 1*_test.py) # Top-level SRAM configurations -TOP_TESTS = \ -19_multi_bank_test.py \ -19_single_bank_test.py \ -20_sram_1bank_test.py \ -20_sram_2bank_test.py \ -20_sram_4bank_test.py +TOP_TESTS = $(shell find ${TEST_DIR} -name 20*_test.py) # All simulation tests. -CHAR_TESTS = \ -21_hspice_delay_test.py \ -21_ngspice_delay_test.py \ -21_ngspice_setuphold_test.py \ -21_hspice_setuphold_test.py \ -22_sram_func_test.py \ -22_pex_func_test_with_pinv.py \ -23_lib_sram_prune_test.py \ -23_lib_sram_test.py +CHAR_TESTS = $(shell find ${TEST_DIR} -name 2[1-2]*_test.py) # Keep the model lib test here since it is fast # and doesn't need simulation. -USAGE_TESTS = \ -23_lib_sram_model_test.py \ -24_lef_sram_test.py \ -25_verilog_sram_test.py +USAGE_TESTS = $(shell find ${TEST_DIR} -name 2[3-9]*_test.py)\ +$(shell find ${TEST_DIR} -name 30*_test.py) -ALL_FILES = \ +ALL_TESTS = \ ${LIBRARY_TESTS} \ ${TECH_TESTS} \ -${PCELLS_TESTS} \ -${MODULES_TESTS} \ +${CELL_TESTS} \ +${MODULE_TESTS} \ ${TOP_TESTS} \ ${CHAR_TESTS} \ ${USAGE_TESTS} -default all: +.PHONY: ${ALL_TESTS} -$(ALL_FILES): - python ${TEST_DIR}/$@ -t freepdk45 - python ${TEST_DIR}/$@ -t scn3me_subm +all: ${ALL_TESTS} # Library tests lib: ${LIBRARY_TESTS} @@ -96,10 +48,10 @@ lib: ${LIBRARY_TESTS} tech: ${TECH_TESTS} # Dynamically generated cells -pcells: ${PCELLS_TESTS} +cell: ${CELL_TESTS} # Dynamically generated modules -modules: ${MODULES_TESTS} +module: ${MODULE_TESTS} # Top level SRAM tests top: ${TOP_TESTS} @@ -110,6 +62,9 @@ char: ${CHAR_TESTS} # Usage and file generation usage: ${USAGE_TESTS} +$(ALL_TESTS): + python3 $@ -t ${TECH} + clean: find . -name \*.pyc -exec rm {} \; find . -name \*~ -exec rm {} \; diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 1d4beb11..db415179 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -73,21 +73,21 @@ class contact(hierarchy_design.hierarchy_design): self.second_layer_name = second_layer def setup_layout_constants(self): - self.contact_width = drc["minwidth_{0}". format(self.via_layer_name)] - contact_to_contact = drc["{0}_to_{0}".format(self.via_layer_name)] + self.contact_width = drc("minwidth_{0}". format(self.via_layer_name)) + contact_to_contact = drc("{0}_to_{0}".format(self.via_layer_name)) self.contact_pitch = self.contact_width + contact_to_contact self.contact_array_width = self.contact_width + (self.dimensions[0] - 1) * self.contact_pitch self.contact_array_height = self.contact_width + (self.dimensions[1] - 1) * self.contact_pitch # DRC rules - first_layer_minwidth = drc["minwidth_{0}".format(self.first_layer_name)] - first_layer_minarea = drc["minarea_{0}".format(self.first_layer_name)] - first_layer_enclosure = drc["{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)] - first_layer_extend = drc["{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)] - second_layer_minwidth = drc["minwidth_{0}".format(self.second_layer_name)] - second_layer_minarea = drc["minarea_{0}".format(self.second_layer_name)] - second_layer_enclosure = drc["{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)] - second_layer_extend = drc["{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)] + first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name)) + first_layer_minarea = drc("minarea_{0}".format(self.first_layer_name)) + first_layer_enclosure = drc("{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)) + first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)) + second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name)) + second_layer_minarea = drc("minarea_{0}".format(self.second_layer_name)) + second_layer_enclosure = drc("{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)) + second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)) self.first_layer_horizontal_enclosure = max((first_layer_minwidth - self.contact_array_width) / 2, first_layer_enclosure) @@ -145,16 +145,16 @@ class contact(hierarchy_design.hierarchy_design): height=self.second_layer_height) def create_implant_well_enclosures(self): - implant_position = self.first_layer_position - [drc["implant_enclosure_active"]]*2 - implant_width = self.first_layer_width + 2*drc["implant_enclosure_active"] - implant_height = self.first_layer_height + 2*drc["implant_enclosure_active"] + implant_position = self.first_layer_position - [drc("implant_enclosure_active")]*2 + implant_width = self.first_layer_width + 2*drc("implant_enclosure_active") + implant_height = self.first_layer_height + 2*drc("implant_enclosure_active") self.add_rect(layer="{}implant".format(self.implant_type), offset=implant_position, width=implant_width, height=implant_height) - well_position = self.first_layer_position - [drc["well_enclosure_active"]]*2 - well_width = self.first_layer_width + 2*drc["well_enclosure_active"] - well_height = self.first_layer_height + 2*drc["well_enclosure_active"] + well_position = self.first_layer_position - [drc("well_enclosure_active")]*2 + well_width = self.first_layer_width + 2*drc("well_enclosure_active") + well_height = self.first_layer_height + 2*drc("well_enclosure_active") self.add_rect(layer="{}well".format(self.well_type), offset=well_position, width=well_width, @@ -172,5 +172,5 @@ active = contact(layer_stack=("active", "contact", "poly")) poly = contact(layer_stack=("poly", "contact", "metal1")) m1m2 = contact(layer_stack=("metal1", "via1", "metal2")) m2m3 = contact(layer_stack=("metal2", "via2", "metal3")) -#m3m4 = contact(layer_stack=("metal3", "via3", "metal4")) +m3m4 = contact(layer_stack=("metal3", "via3", "metal4")) diff --git a/compiler/base/design.py b/compiler/base/design.py index 09522f35..f52aa100 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -18,34 +18,76 @@ class design(hierarchy_design): hierarchy_design.__init__(self,name) self.setup_drc_constants() + self.setup_multiport_constants() self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space, self.m2_space) self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space, self.m3_space) - # SCMOS doesn't have m4... - #self.m3_pitch = max(contact.m3m4.width,contact.m3m4.height) + max(self.m3_space, self.m4_space) - self.m3_pitch = self.m2_pitch + self.m3_pitch = max(contact.m3m4.width,contact.m3m4.height) + max(self.m3_space, self.m4_space) def setup_drc_constants(self): """ These are some DRC constants used in many places in the compiler.""" from tech import drc - self.well_width = drc["minwidth_well"] - self.poly_width = drc["minwidth_poly"] - self.poly_space = drc["poly_to_poly"] - self.m1_width = drc["minwidth_metal1"] - self.m1_space = drc["metal1_to_metal1"] - self.m2_width = drc["minwidth_metal2"] - self.m2_space = drc["metal2_to_metal2"] - self.m3_width = drc["minwidth_metal3"] - self.m3_space = drc["metal3_to_metal3"] - self.active_width = drc["minwidth_active"] - self.contact_width = drc["minwidth_contact"] + self.well_width = drc("minwidth_well") + self.poly_width = drc("minwidth_poly") + self.poly_space = drc("poly_to_poly") + self.m1_width = drc("minwidth_metal1") + self.m1_space = drc("metal1_to_metal1") + self.m2_width = drc("minwidth_metal2") + self.m2_space = drc("metal2_to_metal2") + self.m3_width = drc("minwidth_metal3") + self.m3_space = drc("metal3_to_metal3") + self.m4_width = drc("minwidth_metal4") + self.m4_space = drc("metal4_to_metal4") + self.active_width = drc("minwidth_active") + self.active_space = drc("active_to_body_active") + self.contact_width = drc("minwidth_contact") - self.poly_to_active = drc["poly_to_active"] - self.poly_extend_active = drc["poly_extend_active"] - self.contact_to_gate = drc["contact_to_gate"] - self.well_enclose_active = drc["well_enclosure_active"] - self.implant_enclose_active = drc["implant_enclosure_active"] - self.implant_space = drc["implant_to_implant"] + self.poly_to_active = drc("poly_to_active") + self.poly_extend_active = drc("poly_extend_active") + self.poly_to_polycontact = drc("poly_to_polycontact") + self.contact_to_gate = drc("contact_to_gate") + self.well_enclose_active = drc("well_enclosure_active") + self.implant_enclose_active = drc("implant_enclosure_active") + self.implant_space = drc("implant_to_implant") + + def setup_multiport_constants(self): + """ + These are contants and lists that aid multiport design. + Ports are always in the order RW, W, R. + Port indices start from 0 and increment. + A first RW port will have clk0, csb0, web0, addr0, data0 + A first W port (with no RW ports) will be: clk0, csb0, addr0, data0 + + """ + total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports + + # These are the read/write port indices. + self.readwrite_ports = [] + # These are the read/write and write-only port indices + self.write_ports = [] + # These are the write-only port indices. + self.writeonly_ports = [] + # These are teh read/write and read-only port indice + self.read_ports = [] + # These are the read-only port indices. + self.readonly_ports = [] + # These are all the ports + self.all_ports = list(range(total_ports)) + + port_number = 0 + for port in range(OPTS.num_rw_ports): + self.readwrite_ports.append(port_number) + self.write_ports.append(port_number) + self.read_ports.append(port_number) + port_number += 1 + for port in range(OPTS.num_w_ports): + self.write_ports.append(port_number) + self.writeonly_ports.append(port_number) + port_number += 1 + for port in range(OPTS.num_r_ports): + self.read_ports.append(port_number) + self.readonly_ports.append(port_number) + port_number += 1 def analytical_power(self, proc, vdd, temp, load): """ Get total power of a module """ @@ -53,3 +95,14 @@ class design(hierarchy_design): for inst in self.insts: total_module_power += inst.mod.analytical_power(proc, vdd, temp, load) return total_module_power + + def __str__(self): + """ override print function output """ + pins = ",".join(self.pins) + insts = [" {}".format(x) for x in self.insts] + objs = [" {}".format(x) for x in self.objs] + s = "********** design {0} **********\n".format(self.name) + s += "\n pins ({0})={1}\n".format(len(self.pins), pins) + s += "\n objs ({0})=\n{1}".format(len(self.objs), "\n".join(objs)) + s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts)) + return s diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 7144a2cf..838828df 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -6,6 +6,7 @@ from vector import vector import tech import math from globals import OPTS +from utils import round_to_grid class geometry: """ @@ -46,8 +47,8 @@ class geometry: def normalize(self): """ Re-find the LL and UR points after a transform """ (first,second)=self.boundary - ll = vector(min(first[0],second[0]),min(first[1],second[1])) - ur = vector(max(first[0],second[0]),max(first[1],second[1])) + ll = vector(min(first[0],second[0]),min(first[1],second[1])).snap_to_grid() + ur = vector(max(first[0],second[0]),max(first[1],second[1])).snap_to_grid() self.boundary=[ll,ur] def update_boundary(self): @@ -142,8 +143,16 @@ class instance(geometry): self.rotate = rotate self.offset = vector(offset).snap_to_grid() self.mirror = mirror - self.width = mod.width - self.height = mod.height + if OPTS.netlist_only: + self.width = 0 + self.height = 0 + else: + if mirror in ["R90","R270"] or rotate in [90,270]: + self.width = round_to_grid(mod.height) + self.height = round_to_grid(mod.width) + else: + self.width = round_to_grid(mod.width) + self.height = round_to_grid(mod.height) self.compute_boundary(offset,mirror,rotate) debug.info(4, "creating instance: " + self.name) @@ -191,18 +200,19 @@ class instance(geometry): self.mod.gds_write_file(self.gds) # now write an instance of my module/structure new_layout.addInstance(self.gds, - offsetInMicrons=self.offset, - mirror=self.mirror, - rotate=self.rotate) - + self.mod.name, + offsetInMicrons=self.offset, + mirror=self.mirror, + rotate=self.rotate) + def place(self, offset, mirror="R0", rotate=0): """ This updates the placement of an instance. """ - debug.info(3, "placing instance {}".format(self.name)) # Update the placement of an already added instance - self.offset = vector(offset) + self.offset = vector(offset).snap_to_grid() self.mirror = mirror self.rotate = rotate self.update_boundary() + debug.info(3, "placing instance {}".format(self)) def get_pin(self,name,index=-1): @@ -238,7 +248,7 @@ class instance(geometry): def __str__(self): """ override print function output """ - return "inst: " + self.name + " mod=" + self.mod.name + return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")" def __repr__(self): """ override print function output """ @@ -260,13 +270,13 @@ class path(geometry): # supported right now. It might not work in gdsMill. assert(0) - def gds_write_file(self, newLayout): + def gds_write_file(self, new_layout): """Writes the path to GDS""" debug.info(4, "writing path (" + str(self.layerNumber) + "): " + self.coordinates) - newLayout.addPath(layerNumber=self.layerNumber, - purposeNumber=0, - coordinates=self.coordinates, - width=self.path_width) + new_layout.addPath(layerNumber=self.layerNumber, + purposeNumber=0, + coordinates=self.coordinates, + width=self.path_width) def get_blockages(self, layer): """ Fail since we don't support paths yet. """ @@ -301,15 +311,15 @@ class label(geometry): debug.info(4,"creating label " + self.text + " " + str(self.layerNumber) + " " + str(self.offset)) - def gds_write_file(self, newLayout): + def gds_write_file(self, new_layout): """Writes the text label to GDS""" debug.info(4, "writing label (" + str(self.layerNumber) + "): " + self.text) - newLayout.addText(text=self.text, - layerNumber=self.layerNumber, - purposeNumber=0, - offsetInMicrons=self.offset, - magnification=self.zoom, - rotate=None) + new_layout.addText(text=self.text, + layerNumber=self.layerNumber, + purposeNumber=0, + offsetInMicrons=self.offset, + magnification=self.zoom, + rotate=None) def get_blockages(self, layer): """ Returns an empty list since text cannot be blockages. """ @@ -321,7 +331,7 @@ class label(geometry): def __repr__(self): """ override print function output """ - return "( label: " + self.text + " @" + str(self.offset) + " layer=" + self.layerNumber + " )" + return "( label: " + self.text + " @" + str(self.offset) + " layer=" + str(self.layerNumber) + " )" class rectangle(geometry): """Represents a rectangular shape""" @@ -333,8 +343,8 @@ class rectangle(geometry): self.layerNumber = layerNumber self.offset = vector(offset).snap_to_grid() self.size = vector(width, height).snap_to_grid() - self.width = self.size.x - self.height = self.size.y + self.width = round_to_grid(self.size.x) + self.height = round_to_grid(self.size.y) self.compute_boundary(offset,"",0) debug.info(4, "creating rectangle (" + str(self.layerNumber) + "): " @@ -348,16 +358,16 @@ class rectangle(geometry): else: return [] - def gds_write_file(self, newLayout): + def gds_write_file(self, new_layout): """Writes the rectangular shape to GDS""" debug.info(4, "writing rectangle (" + str(self.layerNumber) + "):" + str(self.width) + "x" + str(self.height) + " @ " + str(self.offset)) - newLayout.addBox(layerNumber=self.layerNumber, - purposeNumber=0, - offsetInMicrons=self.offset, - width=self.width, - height=self.height, - center=False) + new_layout.addBox(layerNumber=self.layerNumber, + purposeNumber=0, + offsetInMicrons=self.offset, + width=self.width, + height=self.height, + center=False) def __str__(self): """ override print function output """ diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 320276cc..6031fe2f 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -6,6 +6,8 @@ import debug import os from globals import OPTS +total_drc_errors = 0 +total_lvs_errors = 0 class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): """ @@ -13,7 +15,6 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): Class consisting of a set of modules and instances of these modules """ name_map = [] - def __init__(self, name): try: @@ -28,26 +29,30 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): self.name = name hierarchy_layout.layout.__init__(self, name) hierarchy_spice.spice.__init__(self, name) - + # Check if the name already exists, if so, give an error # because each reference must be a unique name. # These modules ensure unique names or have no changes if they # aren't unique - ok_list = ['ms_flop', - 'dff', - 'dff_buf', - 'bitcell', - 'contact', + ok_list = ['contact', 'ptx', + 'pbitcell', + 'replica_pbitcell', 'sram', 'hierarchical_predecode2x4', 'hierarchical_predecode3x8'] - if name not in hierarchy_design.name_map: + + # Library cells don't change + if self.is_library_cell: + return + # Name is unique so far + elif name not in hierarchy_design.name_map: hierarchy_design.name_map.append(name) else: + # Name is in our list of exceptions (they don't change) for ok_names in ok_list: - if ok_names in self.__class__.__name__: + if ok_names == self.__class__.__name__: break else: debug.error("Duplicate layout reference name {0} of class {1}. GDS2 requires names be unique.".format(name,self.__class__),-1) @@ -69,36 +74,55 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): """Checks both DRC and LVS for a module""" # Unit tests will check themselves. # Do not run if disabled in options. - if not OPTS.is_unit_test and OPTS.check_lvsdrc: + + if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): + + global total_drc_errors + global total_lvs_errors tempspice = OPTS.openram_temp + "/temp.sp" tempgds = OPTS.openram_temp + "/temp.gds" self.sp_write(tempspice) self.gds_write(tempgds) - debug.check(verify.run_drc(self.name, tempgds) == 0,"DRC failed for {0}".format(self.name)) - debug.check(verify.run_lvs(self.name, tempgds, tempspice, final_verification) == 0,"LVS failed for {0}".format(self.name)) + + num_drc_errors = verify.run_drc(self.name, tempgds, final_verification) + num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification) + debug.check(num_drc_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_drc_errors)) + debug.check(num_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors)) + total_drc_errors += num_drc_errors + total_lvs_errors += num_lvs_errors + os.remove(tempspice) os.remove(tempgds) - def DRC(self): + def DRC(self, final_verification=False): """Checks DRC for a module""" # Unit tests will check themselves. # Do not run if disabled in options. - if not OPTS.is_unit_test and OPTS.check_lvsdrc: + + if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): + global total_drc_errors tempgds = OPTS.openram_temp + "/temp.gds" self.gds_write(tempgds) - debug.check(verify.run_drc(self.name, tempgds) == 0,"DRC failed for {0}".format(self.name)) + num_errors = verify.run_drc(self.name, tempgds, final_verification) + total_drc_errors += num_errors + debug.check(num_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_error)) + os.remove(tempgds) def LVS(self, final_verification=False): """Checks LVS for a module""" # Unit tests will check themselves. # Do not run if disabled in options. - if not OPTS.is_unit_test and OPTS.check_lvsdrc: + + if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): + global total_lvs_errors tempspice = OPTS.openram_temp + "/temp.sp" tempgds = OPTS.openram_temp + "/temp.gds" self.sp_write(tempspice) self.gds_write(tempgds) - debug.check(verify.run_lvs(self.name, tempgds, tempspice, final_verification) == 0,"LVS failed for {0}".format(self.name)) + num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification) + total_lvs_errors += num_errors + debug.check(num_errors == 0,"LVS failed for {0} with {1} error(s)".format(self.name,num_errors)) os.remove(tempspice) os.remove(tempgds) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index b67e910c..c35f0709 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -10,7 +10,7 @@ from vector import vector from pin_layout import pin_layout import lef -class layout(lef.lef): +class layout(): """ Class consisting of a set of objs and instances for a module This provides a set of useful generic types for hierarchy @@ -21,14 +21,13 @@ class layout(lef.lef): """ def __init__(self, name): - lef.lef.__init__(self, ["metal1", "metal2", "metal3"]) self.name = name self.width = None self.height = None self.insts = [] # Holds module/cell layout instances self.objs = [] # Holds all other objects (labels, geometries, etc) self.pin_map = {} # Holds name->pin_layout map for all pins - self.visited = False # Flag for traversing the hierarchy + self.visited = [] # List of modules we have already visited self.is_library_cell = False # Flag for library cells self.gds_read() @@ -134,11 +133,13 @@ class layout(lef.lef): return inst return None - def add_rect(self, layer, offset, width=0, height=0): - """Adds a rectangle on a given layer,offset with width and height""" - if width==0: + def add_rect(self, layer, offset, width=None, height=None): + """ + Adds a rectangle on a given layer,offset with width and height + """ + if not width: width=drc["minwidth_{}".format(layer)] - if height==0: + if not height: height=drc["minwidth_{}".format(layer)] # negative layers indicate "unused" layers in a given technology layer_num = techlayer[layer] @@ -147,11 +148,13 @@ class layout(lef.lef): return self.objs[-1] return None - def add_rect_center(self, layer, offset, width=0, height=0): - """Adds a rectangle on a given layer at the center point with width and height""" - if width==0: + def add_rect_center(self, layer, offset, width=None, height=None): + """ + Adds a rectangle on a given layer at the center point with width and height + """ + if not width: width=drc["minwidth_{}".format(layer)] - if height==0: + if not height: height=drc["minwidth_{}".format(layer)] # negative layers indicate "unused" layers in a given technology layer_num = techlayer[layer] @@ -163,7 +166,9 @@ class layout(lef.lef): def add_segment_center(self, layer, start, end): - """ Add a min-width rectanglular segment using center line on the start to end point """ + """ + Add a min-width rectanglular segment using center line on the start to end point + """ minwidth_layer = drc["minwidth_{}".format(layer)] if start.x!=end.x and start.y!=end.y: debug.error("Nonrectilinear center rect!",-1) @@ -177,7 +182,9 @@ class layout(lef.lef): def get_pin(self, text): - """ Return the pin or list of pins """ + """ + Return the pin or list of pins + """ try: if len(self.pin_map[text])>1: debug.error("Should use a pin iterator since more than one pin {}".format(text),-1) @@ -192,8 +199,13 @@ class layout(lef.lef): def get_pins(self, text): - """ Return a pin list (instead of a single pin) """ - return self.pin_map[text] + """ + Return a pin list (instead of a single pin) + """ + if text in self.pin_map.keys(): + return self.pin_map[text] + else: + return [] def copy_layout_pin(self, instance, pin_name, new_name=""): """ @@ -207,7 +219,9 @@ class layout(lef.lef): self.add_layout_pin(new_name, pin.layer, pin.ll(), pin.width(), pin.height()) def add_layout_pin_segment_center(self, text, layer, start, end): - """ Creates a path like pin with center-line convention """ + """ + Creates a path like pin with center-line convention + """ debug.check(start.x==end.x or start.y==end.y,"Cannot have a non-manhatten layout pin.") @@ -232,9 +246,9 @@ class layout(lef.lef): def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None): """ Creates a path like pin with center-line convention """ - if width==None: + if not width: width=drc["minwidth_{0}".format(layer)] - if height==None: + if not height: height=drc["minwidth_{0}".format(layer)] ll_offset = offset - vector(0.5*width,0.5*height) @@ -243,14 +257,18 @@ class layout(lef.lef): def remove_layout_pin(self, text): - """Delete a labeled pin (or all pins of the same name)""" + """ + Delete a labeled pin (or all pins of the same name) + """ self.pin_map[text]=[] def add_layout_pin(self, text, layer, offset, width=None, height=None): - """Create a labeled pin """ - if width==None: + """ + Create a labeled pin + """ + if not width: width=drc["minwidth_{0}".format(layer)] - if height==None: + if not height: height=drc["minwidth_{0}".format(layer)] new_pin = pin_layout(text, [offset,offset+vector(width,height)], layer) @@ -270,13 +288,14 @@ class layout(lef.lef): return new_pin def add_label_pin(self, text, layer, offset, width=None, height=None): - """Create a labeled pin WITHOUT the pin data structure. This is not an + """ + Create a labeled pin WITHOUT the pin data structure. This is not an actual pin but a named net so that we can add a correspondence point in LVS. """ - if width==None: + if not width: width=drc["minwidth_{0}".format(layer)] - if height==None: + if not height: height=drc["minwidth_{0}".format(layer)] self.add_rect(layer=layer, offset=offset, @@ -313,7 +332,7 @@ class layout(lef.lef): position_list=coordinates, width=width) - def add_route(self, layers, coordinates): + def add_route(self, layers, coordinates, layer_widths): """Connects a routing path on given layer,coordinates,width. The layers are the (horizontal, via, vertical). add_wire assumes preferred direction routing whereas this includes layers in @@ -324,7 +343,8 @@ class layout(lef.lef): # add an instance of our path that breaks down into rectangles and contacts route.route(obj=self, layer_stack=layers, - path=coordinates) + path=coordinates, + layer_widths=layer_widths) def add_wire(self, layers, coordinates): @@ -426,65 +446,76 @@ class layout(lef.lef): def gds_read(self): """Reads a GDSII file in the library and checks if it exists Otherwise, start a new layout for dynamic generation.""" + + # This must be done for netlist only mode too + if os.path.isfile(self.gds_file): + self.is_library_cell=True + if OPTS.netlist_only: self.gds = None return # open the gds file if it exists or else create a blank layout if os.path.isfile(self.gds_file): - debug.info(3, "opening %s" % self.gds_file) - self.is_library_cell=True + debug.info(3, "opening {}".format(self.gds_file)) self.gds = gdsMill.VlsiLayout(units=GDS["unit"]) reader = gdsMill.Gds2reader(self.gds) reader.loadFromFile(self.gds_file) else: - debug.info(4, "creating structure %s" % self.name) + debug.info(3, "Creating layout structure {}".format(self.name)) self.gds = gdsMill.VlsiLayout(name=self.name, units=GDS["unit"]) def print_gds(self, gds_file=None): """Print the gds file (not the vlsi class) to the terminal """ if gds_file == None: gds_file = self.gds_file - debug.info(4, "Printing %s" % gds_file) + debug.info(4, "Printing {}".format(gds_file)) arrayCellLayout = gdsMill.VlsiLayout(units=GDS["unit"]) reader = gdsMill.Gds2reader(arrayCellLayout, debugToTerminal=1) reader.loadFromFile(gds_file) def clear_visited(self): """ Recursively clear the visited flag """ - if not self.visited: - for i in self.insts: - i.mod.clear_visited() - self.visited = False + self.visited = [] - def gds_write_file(self, newLayout): + def gds_write_file(self, gds_layout): """Recursive GDS write function""" # Visited means that we already prepared self.gds for this subtree - if self.visited: + if self.name in self.visited: return for i in self.insts: - i.gds_write_file(newLayout) + i.gds_write_file(gds_layout) for i in self.objs: - i.gds_write_file(newLayout) + i.gds_write_file(gds_layout) for pin_name in self.pin_map.keys(): for pin in self.pin_map[pin_name]: - pin.gds_write_file(newLayout) - self.visited = True + pin.gds_write_file(gds_layout) + self.visited.append(self.name) def gds_write(self, gds_name): """Write the entire gds of the object to the file.""" - debug.info(3, "Writing to {0}".format(gds_name)) + debug.info(3, "Writing to {}".format(gds_name)) + + # If we already wrote a GDS, we need to reset and traverse it again in + # case we made changes. + if not self.is_library_cell and self.visited: + debug.info(3, "Creating layout structure {}".format(self.name)) + self.gds = gdsMill.VlsiLayout(name=self.name, units=GDS["unit"]) writer = gdsMill.Gds2writer(self.gds) # MRG: 3/2/18 We don't want to clear the visited flag since # this would result in duplicates of all instances being placed in self.gds # which may have been previously processed! - #self.clear_visited() + # MRG: 10/4/18 We need to clear if we make changes and write a second GDS! + self.clear_visited() + # recursively create all the remaining objects self.gds_write_file(self.gds) + # populates the xyTree data structure for gds # self.gds.prepareForWrite() writer.writeToFile(gds_name) + debug.info(3, "Done writing to {}".format(gds_name)) def get_boundary(self): """ Return the lower-left and upper-right coordinates of boundary """ @@ -641,11 +672,13 @@ class layout(lef.lef): offset=bus_pos, rotate=90) - def add_horizontal_trunk_route(self, pins, trunk_offset, + def add_horizontal_trunk_route(self, + pins, + trunk_offset, layer_stack=("metal1", "via1", "metal2"), pitch=None): """ - Create a trunk route for all pins with the the trunk located at the given y offset. + Create a trunk route for all pins with the trunk located at the given y offset. """ if not pitch: pitch = self.m1_pitch @@ -672,15 +705,18 @@ class layout(lef.lef): # Route each pin to the trunk for pin in pins: - # Bend to the center of the trunk so it adds a via automatically mid = vector(pin.center().x, trunk_offset.y) - self.add_wire(layer_stack, [pin.center(), mid, trunk_mid]) + self.add_path(layer_stack[2], [pin.center(), mid]) + self.add_via_center(layers=layer_stack, + offset=mid) - def add_vertical_trunk_route(self, pins, trunk_offset, + def add_vertical_trunk_route(self, + pins, + trunk_offset, layer_stack=("metal1", "via1", "metal2"), pitch=None): """ - Create a trunk route for all pins with the the trunk located at the given x offset. + Create a trunk route for all pins with the trunk located at the given x offset. """ if not pitch: pitch = self.m2_pitch @@ -708,54 +744,66 @@ class layout(lef.lef): # Route each pin to the trunk for pin in pins: - # Bend to the center of the trunk so it adds a via automatically mid = vector(trunk_offset.x, pin.center().y) - self.add_wire(layer_stack, [pin.center(), mid, trunk_mid]) + self.add_path(layer_stack[0], [pin.center(), mid]) + self.add_via_center(layers=layer_stack, + offset=mid, + rotate=90) - def create_channel_route(self, netlist, pins, offset, - layer_stack=("metal1", "via1", "metal2"), pitch=None, + def create_channel_route(self, netlist, + offset, + layer_stack=("metal1", "via1", "metal2"), + pitch=None, vertical=False): """ - The net list is a list of the nets. Each net is a list of pin - names to be connected. Pins is a dictionary of the pin names - to the pin structures. Offset is the lower-left of where the + The net list is a list of the nets. Each net is a list of pins + to be connected. Offset is the lower-left of where the routing channel will start. This does NOT try to minimize the number of tracks -- instead, it picks an order to avoid the vertical conflicts between pins. """ def remove_net_from_graph(pin, g): - # Remove the pin from the keys + """ + Remove the pin from the graph and all conflicts + """ g.pop(pin,None) + # Remove the pin from all conflicts - # This is O(n^2), so maybe optimize it. + # FIXME: This is O(n^2), so maybe optimize it. for other_pin,conflicts in g.items(): if pin in conflicts: conflicts.remove(pin) g[other_pin]=conflicts return g - def vcg_pins_overlap(pins1, pins2, vertical): - # Check all the pin pairs on two nets and return a pin - # overlap if any pin overlaps vertically - for pin1 in pins1: - for pin2 in pins2: + def vcg_nets_overlap(net1, net2, vertical): + """ + Check all the pin pairs on two nets and return a pin + overlap if any pin overlaps + """ + + for pin1 in net1: + for pin2 in net2: if vcg_pin_overlap(pin1, pin2, vertical): return True return False def vcg_pin_overlap(pin1, pin2, vertical): - # Check for vertical overlap of the two pins - - # Pin 1 must be in the "TOP" set - x_overlap = pin1.by() > pin2.by() and abs(pin1.center().x-pin2.center().x)0,"Zero width pin.") + debug.check(self.height()>0,"Zero height pin.") + # if it's a layer number look up the layer name. this assumes a unique layer number. if type(layer_name_num)==int: self.layer = list(layer.keys())[list(layer.values()).index(layer_name_num)] @@ -30,9 +35,26 @@ class pin_layout: return "({} layer={} ll={} ur={})".format(self.name,self.layer,self.rect[0],self.rect[1]) def __repr__(self): - """ override print function output """ - return "({} layer={} ll={} ur={})".format(self.name,self.layer,self.rect[0],self.rect[1]) + """ + override repr function output (don't include + name since pin shapes could have same shape but diff name e.g. blockage vs A) + """ + return "(layer={} ll={} ur={})".format(self.layer,self.rect[0],self.rect[1]) + def __hash__(self): + """ Implement the hash function for sets etc. """ + return hash(repr(self)) + + def __lt__(self, other): + """ Provide a function for ordering items by the ll point """ + (ll, ur) = self.rect + (oll, our) = other.rect + + if ll.x < oll.x and ll.y < oll.y: + return True + + return False + def __eq__(self, other): """ Check if these are the same pins for duplicate checks """ if isinstance(other, self.__class__): @@ -40,13 +62,31 @@ class pin_layout: else: return False + def bbox(self, pin_list): + """ + Given a list of layout pins, create a bounding box layout. + """ + (ll, ur) = self.rect + min_x = ll.x + max_x = ur.x + min_y = ll.y + max_y = ur.y + + for pin in pin_list: + min_x = min(min_x, pin.ll().x) + max_x = max(max_x, pin.ur().x) + min_y = min(min_y, pin.ll().y) + max_y = max(max_y, pin.ur().y) + + self.rect = [vector(min_x,min_y),vector(max_x,max_y)] + def inflate(self, spacing=None): """ Inflate the rectangle by the spacing (or other rule) and return the new rectangle. """ if not spacing: - spacing = drc["{0}_to_{0}".format(self.layer)] + spacing = 0.5*drc("{0}_to_{0}".format(self.layer)) (ll,ur) = self.rect spacing = vector(spacing, spacing) @@ -54,21 +94,39 @@ class pin_layout: newur = ur + spacing return (newll, newur) - - def overlaps(self, other): + + def intersection(self, other): """ Check if a shape overlaps with a rectangle """ (ll,ur) = self.rect (oll,our) = other.rect - # Start assuming no overlaps + + min_x = max(ll.x, oll.x) + max_x = min(ll.x, oll.x) + min_y = max(ll.y, oll.y) + max_y = min(ll.y, oll.y) + + return [vector(min_x,min_y),vector(max_x,max_y)] + + def xoverlaps(self, other): + """ Check if shape has x overlap """ + (ll,ur) = self.rect + (oll,our) = other.rect x_overlaps = False - y_overlaps = False # check if self is within other x range if (ll.x >= oll.x and ll.x <= our.x) or (ur.x >= oll.x and ur.x <= our.x): x_overlaps = True # check if other is within self x range if (oll.x >= ll.x and oll.x <= ur.x) or (our.x >= ll.x and our.x <= ur.x): x_overlaps = True - + + return x_overlaps + + def yoverlaps(self, other): + """ Check if shape has x overlap """ + (ll,ur) = self.rect + (oll,our) = other.rect + y_overlaps = False + # check if self is within other y range if (ll.y >= oll.y and ll.y <= our.y) or (ur.y >= oll.y and ur.y <= our.y): y_overlaps = True @@ -76,7 +134,63 @@ class pin_layout: if (oll.y >= ll.y and oll.y <= ur.y) or (our.y >= ll.y and our.y <= ur.y): y_overlaps = True + return y_overlaps + + def xcontains(self, other): + """ Check if shape contains the x overlap """ + (ll,ur) = self.rect + (oll,our) = other.rect + + return (oll.x >= ll.x and our.x <= ur.x) + + def ycontains(self, other): + """ Check if shape contains the y overlap """ + (ll,ur) = self.rect + (oll,our) = other.rect + + return (oll.y >= ll.y and our.y <= ur.y) + + def contains(self, other): + """ Check if a shape contains another rectangle """ + # If it is the same shape entirely, it is contained! + if self == other: + return True + + # Can only overlap on the same layer + if self.layer != other.layer: + return False + + if not self.xcontains(other): + return False + + if not self.ycontains(other): + return False + + return True + + def contained_by_any(self, shape_list): + """ Checks if shape is contained by any in the list """ + for shape in shape_list: + if shape.contains(self): + return True + return False + + + def overlaps(self, other): + """ Check if a shape overlaps with a rectangle """ + # Can only overlap on the same layer + if self.layer != other.layer: + return False + + x_overlaps = self.xoverlaps(other) + y_overlaps = self.yoverlaps(other) + return x_overlaps and y_overlaps + + def area(self): + """ Return the area. """ + return self.height()*self.width() + def height(self): """ Return height. Abs is for pre-normalized value.""" return abs(self.rect[1].y-self.rect[0].y) @@ -204,3 +318,163 @@ class pin_layout: magnification=GDS["zoom"], rotate=None) + + def compute_overlap(self, other): + """ Calculate the rectangular overlap of two rectangles. """ + (r1_ll,r1_ur) = self.rect + (r2_ll,r2_ur) = other.rect + + #ov_ur = vector(min(r1_ur.x,r2_ur.x),min(r1_ur.y,r2_ur.y)) + #ov_ll = vector(max(r1_ll.x,r2_ll.x),max(r1_ll.y,r2_ll.y)) + + dy = min(r1_ur.y,r2_ur.y)-max(r1_ll.y,r2_ll.y) + dx = min(r1_ur.x,r2_ur.x)-max(r1_ll.x,r2_ll.x) + + if dx>=0 and dy>=0: + return [dx,dy] + else: + return [0,0] + + def distance(self, other): + """ + Calculate the distance to another pin layout. + """ + (r1_ll,r1_ur) = self.rect + (r2_ll,r2_ur) = other.rect + + def dist(x1, y1, x2, y2): + return math.sqrt((x2-x1)**2 + (y2-y1)**2) + + left = r2_ur.x < r1_ll.x + right = r1_ur.x < r2_ll.x + bottom = r2_ur.y < r1_ll.y + top = r1_ur.y < r2_ll.y + + if top and left: + return dist(r1_ll.x, r1_ur.y, r2_ur.x, r2_ll.y) + elif left and bottom: + return dist(r1_ll.x, r1_ll.y, r2_ur.x, r2_ur.y) + elif bottom and right: + return dist(r1_ur.x, r1_ll.y, r2_ll.x, r2_ur.y) + elif right and top: + return dist(r1_ur.x, r1_ur.y, r2_ll.x, r2_ll.y) + elif left: + return r1_ll.x - r2_ur.x + elif right: + return r2_ll.x - r1.ur.x + elif bottom: + return r1_ll.y - r2_ur.y + elif top: + return r2_ll.y - r1_ur.y + else: + # rectangles intersect + return 0 + + + def overlap_length(self, other): + """ + Calculate the intersection segment and determine its length + """ + + if self.contains(other): + return math.inf + elif other.contains(self): + return math.inf + else: + intersections = self.compute_overlap_segment(other) + # This is the common case where two pairs of edges overlap + # at two points, so just find the distance between those two points + if len(intersections)==2: + (p1,p2) = intersections + return math.sqrt(pow(p1[0]-p2[0],2) + pow(p1[1]-p2[1],2)) + else: + # This is where we had a corner intersection or none + return 0 + + + def compute_overlap_segment(self, other): + """ + Calculate the intersection segment of two rectangles + (if any) + """ + (r1_ll,r1_ur) = self.rect + (r2_ll,r2_ur) = other.rect + + # The other corners besides ll and ur + r1_ul = vector(r1_ll.x, r1_ur.y) + r1_lr = vector(r1_ur.x, r1_ll.y) + r2_ul = vector(r2_ll.x, r2_ur.y) + r2_lr = vector(r2_ur.x, r2_ll.y) + + from itertools import tee + def pairwise(iterable): + "s -> (s0,s1), (s1,s2), (s2, s3), ..." + a, b = tee(iterable) + next(b, None) + return zip(a, b) + + # R1 edges CW + r1_cw_points = [r1_ll, r1_ul, r1_ur, r1_lr, r1_ll] + r1_edges = [] + for (p,q) in pairwise(r1_cw_points): + r1_edges.append([p,q]) + + # R2 edges CW + r2_cw_points = [r2_ll, r2_ul, r2_ur, r2_lr, r2_ll] + r2_edges = [] + for (p,q) in pairwise(r2_cw_points): + r2_edges.append([p,q]) + + # There are 4 edges on each rectangle + # so just brute force check intersection of each + # Two pairs of them should intersect + intersections = [] + for r1e in r1_edges: + for r2e in r2_edges: + i = self.segment_intersection(r1e, r2e) + if i: + intersections.append(i) + + return intersections + + def on_segment(self, p, q, r): + """ + Given three co-linear points, determine if q lies on segment pr + """ + if q.x <= max(p.x, r.x) and \ + q.x >= min(p.x, r.x) and \ + q.y <= max(p.y, r.y) and \ + q.y >= min(p.y, r.y): + return True + + return False + + def segment_intersection(self, s1, s2): + """ + Determine the intersection point of two segments + Return the a segment if they overlap. + Return None if they don't. + """ + (a,b) = s1 + (c,d) = s2 + # Line AB represented as a1x + b1y = c1 + a1 = b.y - a.y + b1 = a.x - b.x + c1 = a1*a.x + b1*a.y + + # Line CD represented as a2x + b2y = c2 + a2 = d.y - c.y + b2 = c.x - d.x + c2 = a2*c.x + b2*c.y + + determinant = a1*b2 - a2*b1 + + if determinant!=0: + x = (b2*c1 - b1*c2)/determinant + y = (a1*c2 - a2*c1)/determinant + + r = vector(x,y).snap_to_grid() + if self.on_segment(a, r, b) and self.on_segment(c, r, d): + return r + + return None diff --git a/compiler/base/route.py b/compiler/base/route.py index beff1a58..09925e07 100644 --- a/compiler/base/route.py +++ b/compiler/base/route.py @@ -10,15 +10,17 @@ class route(design): """ Object route (used by the router module) Add a route of minimium metal width between a set of points. + The widths are the layer widths of the layer stack. + (Vias are in numer of vias.) The wire must be completely rectilinear and the - z-dimension of the points refers to the layers (plus via) + z-dimension of the points refers to the layers. The points are the center of the wire. This can have non-preferred direction routing. """ unique_route_id = 0 - def __init__(self, obj, layer_stack, path): + def __init__(self, obj, layer_stack, path, layer_widths=[None,1,None]): name = "route_{0}".format(route.unique_route_id) route.unique_route_id += 1 design.__init__(self, name) @@ -26,6 +28,7 @@ class route(design): self.obj = obj self.layer_stack = layer_stack + self.layer_widths = layer_widths self.path = path self.setup_layers() @@ -33,16 +36,16 @@ class route(design): def setup_layers(self): - (horiz_layer, via_layer, vert_layer) = self.layer_stack - self.via_layer_name = via_layer - - self.vert_layer_name = vert_layer - self.vert_layer_width = drc["minwidth_{0}".format(vert_layer)] - - self.horiz_layer_name = horiz_layer - self.horiz_layer_width = drc["minwidth_{0}".format(horiz_layer)] + (self.horiz_layer_name, self.via_layer, self.vert_layer_name) = self.layer_stack + (self.horiz_layer_width, self.num_vias, self.vert_layer_width) = self.layer_widths + + if not self.vert_layer_width: + self.vert_layer_width = drc("minwidth_{0}".format(self.vert_layer_name)) + if not self.horiz_layer_width: + self.horiz_layer_width = drc("minwidth_{0}".format(self.horiz_layer_name)) + # offset this by 1/2 the via size - self.c=contact(self.layer_stack, (1, 1)) + self.c=contact(self.layer_stack, (self.num_vias, self.num_vias)) def create_wires(self): @@ -59,13 +62,10 @@ class route(design): plist = list(pairwise(self.path)) for p0,p1 in plist: if p0.z != p1.z: # via - # offset if not rotated - #via_offset = vector(p0.x+0.5*self.c.width,p0.y+0.5*self.c.height) - # offset if rotated - via_offset = vector(p0.x+0.5*self.c.height,p0.y-0.5*self.c.width) - self.obj.add_via(self.layer_stack,via_offset,rotate=90) + via_size = [self.num_vias]*2 + self.obj.add_via_center(self.layer_stack,vector(p0.x,p0.y),size=via_size,rotate=90) elif p0.x != p1.x and p0.y != p1.y: # diagonal! - debug.error("Non-changing direction!") + debug.error("Diagonal route! {}".format(self.path),-3) else: # this will draw an extra corner at the end but that is ok self.draw_corner_wire(p1) @@ -79,14 +79,36 @@ class route(design): self.draw_corner_wire(plist[-1][1]) - + def get_layer_width(self, layer_zindex): + """ + Return the layer width + """ + if layer_zindex==0: + return self.horiz_layer_width + elif layer_zindex==1: + return self.vert_layer_width + else: + debug.error("Incorrect layer zindex.",-1) + + def get_layer_name(self, layer_zindex): + """ + Return the layer name + """ + if layer_zindex==0: + return self.horiz_layer_name + elif layer_zindex==1: + return self.vert_layer_name + else: + debug.error("Incorrect layer zindex.",-1) + def draw_wire(self, p0, p1): """ This draws a straight wire with layer_minwidth """ - layer_name = self.layer_stack[2*p0.z] - layer_width = drc["minwidth_{0}".format(layer_name)] + + layer_width = self.get_layer_width(p0.z) + layer_name = self.get_layer_name(p0.z) # always route left to right or bottom to top if p0.z != p1.z: @@ -120,8 +142,8 @@ class route(design): """ This function adds the corner squares since the center line convention only draws to the center of the corner.""" - layer_name = self.layer_stack[2*p0.z] - layer_width = drc["minwidth_{0}".format(layer_name)] + layer_width = self.get_layer_width(p0.z) + layer_name = self.get_layer_name(p0.z) offset = vector(p0.x-0.5*layer_width,p0.y-0.5*layer_width) self.obj.add_rect(layer=layer_name, offset=offset, diff --git a/compiler/base/utils.py b/compiler/base/utils.py index b13f2f7e..8ca22fa9 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -3,6 +3,7 @@ import gdsMill import tech import math import globals +import debug from vector import vector from pin_layout import pin_layout @@ -65,6 +66,7 @@ def get_gds_size(name, gds_filename, units, layer): Open a GDS file and return the size from either the bounding box or a border layer. """ + debug.info(4,"Creating VLSI layout for {}".format(name)) cell_vlsi = gdsMill.VlsiLayout(units=units) reader = gdsMill.Gds2reader(cell_vlsi) reader.loadFromFile(gds_filename) @@ -72,6 +74,7 @@ def get_gds_size(name, gds_filename, units, layer): cell = {} measure_result = cell_vlsi.getLayoutBorder(layer) if measure_result == None: + debug.info(2,"Layout border failed. Trying to measure size for {}".format(name)) measure_result = cell_vlsi.measureSize(name) # returns width,height return measure_result @@ -85,9 +88,9 @@ def get_libcell_size(name, units, layer): return(get_gds_size(name, cell_gds, units, layer)) -def get_gds_pins(pin_list, name, gds_filename, units, layer): +def get_gds_pins(pin_names, name, gds_filename, units): """ - Open a GDS file and find the pins in pin_list as text on a given layer. + Open a GDS file and find the pins in pin_names as text on a given layer. Return these as a rectangle layer pair for each pin. """ cell_vlsi = gdsMill.VlsiLayout(units=units) @@ -95,23 +98,23 @@ def get_gds_pins(pin_list, name, gds_filename, units, layer): reader.loadFromFile(gds_filename) cell = {} - for pin in pin_list: - cell[str(pin)]=[] - label_list=cell_vlsi.getPinShapeByLabel(str(pin)) - for label in label_list: - (name,layer,boundary)=label + for pin_name in pin_names: + cell[str(pin_name)]=[] + pin_list=cell_vlsi.getPinShape(str(pin_name)) + for pin_shape in pin_list: + (layer,boundary)=pin_shape rect=[vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])] # this is a list because other cells/designs may have must-connect pins - cell[str(pin)].append(pin_layout(pin, rect, layer)) + cell[str(pin_name)].append(pin_layout(pin_name, rect, layer)) return cell -def get_libcell_pins(pin_list, name, units, layer): +def get_libcell_pins(pin_list, name, units): """ Open a GDS file and find the pins in pin_list as text on a given layer. Return these as a rectangle layer pair for each pin. """ cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" - return(get_gds_pins(pin_list, name, cell_gds, units, layer)) + return(get_gds_pins(pin_list, name, cell_gds, units)) diff --git a/compiler/base/vector.py b/compiler/base/vector.py index 3d79b90a..6069a1ab 100644 --- a/compiler/base/vector.py +++ b/compiler/base/vector.py @@ -11,24 +11,24 @@ class vector(): concise vector operations, output, and other more complex data structures like lists. """ - def __init__(self, x, y=None): + def __init__(self, x, y=0): """ init function support two init method""" # will take single input as a coordinate - if y==None: - self.x = x[0] - self.y = x[1] + if isinstance(x, (list,tuple,vector)): + self.x = float(x[0]) + self.y = float(x[1]) #will take two inputs as the values of a coordinate else: - self.x = x - self.y = y + self.x = float(x) + self.y = float(y) def __str__(self): """ override print function output """ - return "["+str(self.x)+","+str(self.y)+"]" + return "v["+str(self.x)+","+str(self.y)+"]" def __repr__(self): """ override print function output """ - return "["+str(self.x)+","+str(self.y)+"]" + return "v["+str(self.x)+","+str(self.y)+"]" def __setitem__(self, index, value): """ @@ -36,12 +36,12 @@ class vector(): can set value by vector[index]=value """ if index==0: - self.x=value + self.x=float(value) elif index==1: - self.y=value + self.y=float(value) else: - self.x=value[0] - self.y=value[1] + self.x=float(value[0]) + self.y=float(value[1]) def __getitem__(self, index): """ @@ -84,6 +84,14 @@ class vector(): """ return vector(other[0]- self.x, other[1] - self.y) + def __hash__(self): + """ + Override - function (hash) + Note: This assumes that you DON'T CHANGE THE VECTOR or it will + break things. + """ + return hash((self.x,self.y)) + def snap_to_grid(self): self.x = self.snap_offset_to_grid(self.x) self.y = self.snap_offset_to_grid(self.y) diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index c4bbcd0c..a2ddcadf 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -1,60 +1,165 @@ import debug class verilog: - """ Create a behavioral Verilog file for simulation.""" - + """ + Create a behavioral Verilog file for simulation. + This is inherited by the sram_base class. + """ + def __init__(self): + pass + def verilog_write(self,verilog_name): """ Write a behavioral Verilog model. """ - self.vf = open(verilog_name, "w") self.vf.write("// OpenRAM SRAM model\n") self.vf.write("// Words: {0}\n".format(self.num_words)) self.vf.write("// Word size: {0}\n\n".format(self.word_size)) - - self.vf.write("module {0}(DATA,ADDR,CSb,WEb,OEb,clk);\n".format(self.name)) - self.vf.write("\n") + + self.vf.write("module {0}(\n".format(self.name)) + for port in self.all_ports: + if port in self.readwrite_ports: + self.vf.write("// Port {0}: RW\n".format(port)) + elif port in self.read_ports: + self.vf.write("// Port {0}: R\n".format(port)) + elif port in self.write_ports: + self.vf.write("// Port {0}: W\n".format(port)) + if port in self.readwrite_ports: + self.vf.write(" clk{0},csb{0},web{0},ADDR{0},DIN{0},DOUT{0}".format(port)) + elif port in self.write_ports: + self.vf.write(" clk{0},csb{0},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)) + # Continue for every port on a new line + if port != self.all_ports[-1]: + self.vf.write(",\n") + self.vf.write("\n );\n\n") + self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size)) 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") self.vf.write(" parameter DELAY = 3 ;\n") - self.vf.write("\n") - self.vf.write(" inout [DATA_WIDTH-1:0] DATA;\n") - self.vf.write(" input [ADDR_WIDTH-1:0] ADDR;\n") - self.vf.write(" input CSb; // active low chip select\n") - self.vf.write(" input WEb; // active low write control\n") - self.vf.write(" input OEb; // active output enable\n") - self.vf.write(" input clk; // clock\n") self.vf.write("\n") - self.vf.write(" reg [DATA_WIDTH-1:0] data_out ;\n") - self.vf.write(" reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];\n") + + for port in self.all_ports: + self.add_inputs_outputs(port) + self.vf.write("\n") - self.vf.write(" // Tri-State Buffer control\n") - self.vf.write(" // output : When WEb = 1, oeb = 0, csb = 0\n") - self.vf.write(" assign DATA = (!CSb && !OEb && WEb) ? data_out : {0}'bz;\n".format(self.word_size)) - self.vf.write("\n") - self.vf.write(" // Memory Write Block\n") - self.vf.write(" // Write Operation : When WEb = 0, CSb = 0\n") - self.vf.write(" always @ (posedge clk)\n") - self.vf.write(" begin : MEM_WRITE\n") - self.vf.write(" if ( !CSb && !WEb ) begin\n") - self.vf.write(" mem[ADDR] = DATA;\n") - self.vf.write(" $display($time,\" Writing %m ABUS=%b DATA=%b\",ADDR,DATA);\n") - self.vf.write(" end\n") - self.vf.write(" end\n\n") - self.vf.write("\n") - self.vf.write(" // Memory Read Block\n") - self.vf.write(" // Read Operation : When WEb = 1, CSb = 0\n") - self.vf.write(" always @ (posedge clk)\n") - self.vf.write(" begin : MEM_READ\n") - self.vf.write(" if (!CSb && WEb) begin\n") - self.vf.write(" data_out <= #(DELAY) mem[ADDR];\n") - self.vf.write(" $display($time,\" Reading %m ABUS=%b DATA=%b\",ADDR,mem[ADDR]);\n") - self.vf.write(" end\n") - self.vf.write(" end\n") + + for port in self.all_ports: + self.register_inputs(port) + + # This is the memory array itself + self.vf.write("reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];\n") + + for port in self.all_ports: + if port in self.write_ports: + self.add_write_block(port) + if port in self.read_ports: + self.add_read_block(port) + self.vf.write("\n") self.vf.write("endmodule\n") - - self.vf.close() + + def register_inputs(self, port): + """ + Register the control signal, address and data inputs. + """ + self.add_regs(port) + self.add_flops(port) + + def add_regs(self, port): + """ + Create the input regs for the given port. + """ + self.vf.write(" reg csb{0}_reg;\n".format(port)) + if port in self.readwrite_ports: + self.vf.write(" reg web{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)) + if port in self.read_ports: + self.vf.write(" reg [DATA_WIDTH-1:0] DOUT{0};\n".format(port)) + + def add_flops(self, port): + """ + Add the flop behavior logic for a port. + """ + self.vf.write("\n") + self.vf.write(" // All inputs are registers\n") + self.vf.write(" always @(posedge clk{0})\n".format(port)) + self.vf.write(" begin\n") + self.vf.write(" csb{0}_reg = csb{0};\n".format(port)) + if port in self.readwrite_ports: + self.vf.write(" web{0}_reg = web{0};\n".format(port)) + self.vf.write(" ADDR{0}_reg = ADDR{0};\n".format(port)) + if port in self.write_ports: + self.vf.write(" DIN{0}_reg = DIN{0};\n".format(port)) + if port in self.read_ports: + self.vf.write(" DOUT{0} = {1}'bx;\n".format(port,self.word_size)) + if port in self.readwrite_ports: + self.vf.write(" if ( !csb{0}_reg && web{0}_reg ) \n".format(port)) + self.vf.write(" $display($time,\" Reading %m ADDR{0}=%b DOUT{0}=%b\",ADDR{0}_reg,mem[ADDR{0}_reg]);\n".format(port)) + elif port in self.read_ports: + self.vf.write(" if ( !csb{0}_reg ) \n".format(port)) + self.vf.write(" $display($time,\" Reading %m ADDR{0}=%b DOUT{0}=%b\",ADDR{0}_reg,mem[ADDR{0}_reg]);\n".format(port)) + + if port in self.readwrite_ports: + self.vf.write(" if ( !csb{0}_reg && !web{0}_reg )\n".format(port)) + self.vf.write(" $display($time,\" Writing %m ADDR{0}=%b DIN{0}=%b\",ADDR{0}_reg,DIN{0}_reg);\n".format(port)) + elif port in self.write_ports: + self.vf.write(" if ( !csb{0}_reg )\n".format(port)) + self.vf.write(" $display($time,\" Writing %m ADDR{0}=%b DIN{0}=%b\",ADDR{0}_reg,DIN{0}_reg);\n".format(port)) + self.vf.write(" end\n\n") + + + def add_inputs_outputs(self, port): + """ + Add the module input and output declaration for a port. + """ + self.vf.write(" input clk{0}; // clock\n".format(port)) + self.vf.write(" input csb{0}; // active low chip select\n".format(port)) + if port in self.readwrite_ports: + self.vf.write(" input web{0}; // active low write control\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)) + if port in self.read_ports: + self.vf.write(" output [DATA_WIDTH-1:0] DOUT{0};\n".format(port)) + + def add_write_block(self, port): + """ + Add a write port block. Multiple simultaneous writes to the same address + have arbitrary priority and are not allowed. + """ + self.vf.write("\n") + self.vf.write(" // Memory Write Block Port {0}\n".format(port)) + self.vf.write(" // Write Operation : When web{0} = 0, csb{0} = 0\n".format(port)) + 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: + self.vf.write(" if ( !csb{0}_reg && !web{0}_reg )\n".format(port)) + else: + self.vf.write(" if (!csb{0}_reg)\n".format(port)) + self.vf.write(" mem[ADDR{0}_reg] = DIN{0}_reg;\n".format(port)) + self.vf.write(" end\n") + + def add_read_block(self, port): + """ + Add a read port block. + """ + self.vf.write("\n") + self.vf.write(" // Memory Read Block Port {0}\n".format(port)) + self.vf.write(" // Read Operation : When web{0} = 1, csb{0} = 0\n".format(port)) + self.vf.write(" always @ (negedge clk{0})\n".format(port)) + self.vf.write(" begin : MEM_READ{0}\n".format(port)) + if port in self.readwrite_ports: + self.vf.write(" if (!csb{0}_reg && web{0}_reg)\n".format(port)) + else: + self.vf.write(" if (!csb{0}_reg)\n".format(port)) + self.vf.write(" DOUT{0} <= #(DELAY) mem[ADDR{0}_reg];\n".format(port)) + self.vf.write(" end\n") + diff --git a/compiler/base/wire.py b/compiler/base/wire.py index 9220c77a..1565d10e 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -33,14 +33,14 @@ class wire(path): self.via_layer_name = via_layer self.vert_layer_name = vert_layer - self.vert_layer_width = drc["minwidth_{0}".format(vert_layer)] + self.vert_layer_width = drc("minwidth_{0}".format(vert_layer)) self.horiz_layer_name = horiz_layer - self.horiz_layer_width = drc["minwidth_{0}".format(horiz_layer)] + self.horiz_layer_width = drc("minwidth_{0}".format(horiz_layer)) via_connect = contact(self.layer_stack, (1, 1)) - self.node_to_node = [drc["minwidth_" + str(self.horiz_layer_name)] + via_connect.width, - drc["minwidth_" + str(self.horiz_layer_name)] + via_connect.height] + self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width, + drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height] # create a 1x1 contact def create_vias(self): diff --git a/compiler/modules/bitcell.py b/compiler/bitcells/bitcell.py similarity index 73% rename from compiler/modules/bitcell.py rename to compiler/bitcells/bitcell.py index 4e741e24..0ac1c207 100644 --- a/compiler/modules/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -1,7 +1,7 @@ import design import debug import utils -from tech import GDS,layer +from tech import GDS,layer,parameter,drc class bitcell(design.design): """ @@ -13,7 +13,7 @@ class bitcell(design.design): pin_names = ["bl", "br", "wl", "vdd", "gnd"] (width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"]) def __init__(self): design.design.__init__(self, "cell_6t") @@ -38,9 +38,9 @@ class bitcell(design.design): def list_bitcell_pins(self, col, row): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ - bitcell_pins = ["bl[{0}]".format(col), - "br[{0}]".format(col), - "wl[{0}]".format(row), + bitcell_pins = ["bl_{0}".format(col), + "br_{0}".format(col), + "wl_{0}".format(row), "vdd", "gnd"] return bitcell_pins @@ -65,26 +65,6 @@ class bitcell(design.design): column_pins = ["br"] return column_pins - def list_read_bl_names(self): - """ Creates a list of bl pin names associated with read ports """ - column_pins = ["bl"] - return column_pins - - def list_read_br_names(self): - """ Creates a list of br pin names associated with read ports """ - column_pins = ["br"] - return column_pins - - def list_write_bl_names(self): - """ Creates a list of bl pin names associated with write ports """ - column_pins = ["bl"] - return column_pins - - def list_write_br_names(self): - """ Creates a list of br pin names asscociated with write ports""" - column_pins = ["br"] - return column_pins - def analytical_power(self, proc, vdd, temp, load): """Bitcell power in nW. Only characterizes leakage.""" from tech import spice @@ -93,3 +73,9 @@ class bitcell(design.design): total_power = self.return_power(dynamic, leakage) return total_power + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #This is a handmade cell so the value must be entered in the tech.py file or estimated. + #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. + access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] + return 2*access_tx_cin diff --git a/compiler/bitcells/bitcell_1rw_1r.py b/compiler/bitcells/bitcell_1rw_1r.py new file mode 100644 index 00000000..2d740d2b --- /dev/null +++ b/compiler/bitcells/bitcell_1rw_1r.py @@ -0,0 +1,105 @@ +import design +import debug +import utils +from tech import GDS,layer,parameter,drc + +class bitcell_1rw_1r(design.design): + """ + A single bit cell (6T, 8T, etc.) This module implements the + single memory cell used in the design. It is a hand-made cell, so + the layout and netlist should be available in the technology + library. + """ + + pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + (width,height) = utils.get_libcell_size("cell_1rw_1r", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"]) + + def __init__(self): + design.design.__init__(self, "cell_1rw_1r") + debug.info(2, "Create bitcell with 1RW and 1R Port") + + self.width = bitcell_1rw_1r.width + self.height = bitcell_1rw_1r.height + self.pin_map = bitcell_1rw_1r.pin_map + + def analytical_delay(self, slew, load=0, swing = 0.5): + # delay of bit cell is not like a driver(from WL) + # so the slew used should be 0 + # it should not be slew dependent? + # because the value is there + # the delay is only over half transsmission gate + from tech import spice + r = spice["min_tx_r"]*3 + c_para = spice["min_tx_drain_c"] + result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew, swing = swing) + return result + + + def list_bitcell_pins(self, col, row): + """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ + bitcell_pins = ["bl0_{0}".format(col), + "br0_{0}".format(col), + "bl1_{0}".format(col), + "br1_{0}".format(col), + "wl0_{0}".format(row), + "wl1_{0}".format(row), + "vdd", + "gnd"] + return bitcell_pins + + def list_all_wl_names(self): + """ Creates a list of all wordline pin names """ + row_pins = ["wl0", "wl1"] + return row_pins + + def list_all_bitline_names(self): + """ Creates a list of all bitline pin names (both bl and br) """ + column_pins = ["bl0", "br0", "bl1", "br1"] + return column_pins + + def list_all_bl_names(self): + """ Creates a list of all bl pins names """ + column_pins = ["bl0", "bl1"] + return column_pins + + def list_all_br_names(self): + """ Creates a list of all br pins names """ + column_pins = ["br0", "br1"] + return column_pins + + def list_read_bl_names(self): + """ Creates a list of bl pin names associated with read ports """ + column_pins = ["bl0", "bl1"] + return column_pins + + def list_read_br_names(self): + """ Creates a list of br pin names associated with read ports """ + column_pins = ["br0", "br1"] + return column_pins + + def list_write_bl_names(self): + """ Creates a list of bl pin names associated with write ports """ + column_pins = ["bl0"] + return column_pins + + def list_write_br_names(self): + """ Creates a list of br pin names asscociated with write ports""" + column_pins = ["br0"] + return column_pins + + def analytical_power(self, proc, vdd, temp, load): + """Bitcell power in nW. Only characterizes leakage.""" + from tech import spice + leakage = spice["bitcell_leakage"] + dynamic = 0 #temporary + total_power = self.return_power(dynamic, leakage) + return total_power + + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #This is a handmade cell so the value must be entered in the tech.py file or estimated. + #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. + #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] + return 2*access_tx_cin \ No newline at end of file diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py new file mode 100644 index 00000000..52156b39 --- /dev/null +++ b/compiler/bitcells/pbitcell.py @@ -0,0 +1,896 @@ +import contact +import design +import debug +from tech import drc, parameter, spice +from vector import vector +from ptx import ptx +from globals import OPTS + +class pbitcell(design.design): + """ + This module implements a parametrically sized multi-port bitcell, + with a variable number of read/write, write, and read ports + """ + + def __init__(self, replica_bitcell=False): + + self.num_rw_ports = OPTS.num_rw_ports + self.num_w_ports = OPTS.num_w_ports + self.num_r_ports = OPTS.num_r_ports + self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports + + self.replica_bitcell = replica_bitcell + + if self.replica_bitcell: + name = "replica_pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports) + else: + name = "pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports) + + design.design.__init__(self, name) + debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, + self.num_w_ports, + self.num_r_ports)) + + self.create_netlist() + # We must always create the bitcell layout because some transistor sizes in the other netlists depend on it + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_storage() + + if(self.num_rw_ports > 0): + self.create_readwrite_ports() + if(self.num_w_ports > 0): + self.create_write_ports() + if(self.num_r_ports > 0): + self.create_read_ports() + + def create_layout(self): + self.calculate_spacing() + self.calculate_postions() + + self.place_storage() + self.route_storage() + + self.route_rails() + + if(self.num_rw_ports > 0): + self.place_readwrite_ports() + self.route_readwrite_access() + if(self.num_w_ports > 0): + self.place_write_ports() + self.route_write_access() + if(self.num_r_ports > 0): + self.place_read_ports() + self.route_read_access() + self.extend_well() + + self.route_wordlines() + self.route_bitlines() + self.route_supply() + + if self.replica_bitcell: + self.route_rbc_short() + + # in netlist_only mode, calling offset_all_coordinates or translate_all will not be possible + # this function is not needed to calculate the dimensions of pbitcell in netlist_only mode though + if not OPTS.netlist_only: + self.offset_all_coordinates() + gnd_overlap = vector(0, 0.5*contact.well.width) + self.translate_all(gnd_overlap) + + + def add_pins(self): + """ add pins and set names for bitlines and wordlines """ + self.rw_bl_names = [] + self.rw_br_names = [] + self.w_bl_names = [] + self.w_br_names = [] + self.r_bl_names = [] + self.r_br_names = [] + self.rw_wl_names = [] + self.w_wl_names = [] + self.r_wl_names = [] + port = 0 + + for k in range(self.num_rw_ports): + self.add_pin("bl{}".format(port)) + self.add_pin("br{}".format(port)) + self.rw_bl_names.append("bl{}".format(port)) + self.rw_br_names.append("br{}".format(port)) + port += 1 + for k in range(self.num_w_ports): + self.add_pin("bl{}".format(port)) + self.add_pin("br{}".format(port)) + self.w_bl_names.append("bl{}".format(port)) + self.w_br_names.append("br{}".format(port)) + port += 1 + for k in range(self.num_r_ports): + self.add_pin("bl{}".format(port)) + self.add_pin("br{}".format(port)) + self.r_bl_names.append("bl{}".format(port)) + self.r_br_names.append("br{}".format(port)) + port += 1 + + port = 0 + for k in range(self.num_rw_ports): + self.add_pin("wl{}".format(port)) + self.rw_wl_names.append("wl{}".format(port)) + port += 1 + for k in range(self.num_w_ports): + self.add_pin("wl{}".format(port)) + self.w_wl_names.append("wl{}".format(port)) + port += 1 + for k in range(self.num_r_ports): + self.add_pin("wl{}".format(port)) + self.r_wl_names.append("wl{}".format(port)) + port += 1 + + self.add_pin("vdd") + self.add_pin("gnd") + + # if this is a replica bitcell, replace the instances of Q_bar with vdd + if self.replica_bitcell: + self.Q_bar = "vdd" + else: + self.Q_bar = "Q_bar" + + def add_modules(self): + """ Determine size of transistors and add ptx modules """ + # if there are any read/write ports, then the inverter nmos is sized based the number of read/write ports + if(self.num_rw_ports > 0): + inverter_nmos_width = self.num_rw_ports*parameter["6T_inv_nmos_size"] + inverter_pmos_width = parameter["6T_inv_pmos_size"] + readwrite_nmos_width = parameter["6T_access_size"] + write_nmos_width = parameter["6T_access_size"] + read_nmos_width = 2*parameter["6T_inv_pmos_size"] + + # if there are no read/write ports, then the inverter nmos is statically sized for the dual port case + else: + inverter_nmos_width = 2*parameter["6T_inv_pmos_size"] + inverter_pmos_width = parameter["6T_inv_pmos_size"] + readwrite_nmos_width = parameter["6T_access_size"] + write_nmos_width = parameter["6T_access_size"] + read_nmos_width = 2*parameter["6T_inv_pmos_size"] + + # create ptx for inverter transistors + self.inverter_nmos = ptx(width=inverter_nmos_width, + tx_type="nmos") + self.add_mod(self.inverter_nmos) + + self.inverter_pmos = ptx(width=inverter_pmos_width, + tx_type="pmos") + self.add_mod(self.inverter_pmos) + + # create ptx for readwrite transitors + self.readwrite_nmos = ptx(width=readwrite_nmos_width, + tx_type="nmos") + self.add_mod(self.readwrite_nmos) + + # create ptx for write transitors + self.write_nmos = ptx(width=write_nmos_width, + tx_type="nmos") + self.add_mod(self.write_nmos) + + # create ptx for read transistors + self.read_nmos = ptx(width=read_nmos_width, + tx_type="nmos") + self.add_mod(self.read_nmos) + + def calculate_spacing(self): + """ Calculate transistor spacings """ + # calculate metal contact extensions over transistor active + readwrite_nmos_contact_extension = 0.5*(self.readwrite_nmos.active_contact.height - self.readwrite_nmos.active_height) + write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height) + read_nmos_contact_extension = 0.5*(self.read_nmos.active_contact.height - self.read_nmos.active_height) + max_contact_extension = max(readwrite_nmos_contact_extension, write_nmos_contact_extension, read_nmos_contact_extension) + + # y-offset for the access transistor's gate contact + self.gate_contact_yoffset = max_contact_extension + self.m2_space + 0.5*max(contact.poly.height, contact.m1m2.height) + + # y-position of access transistors + self.port_ypos = self.m1_space + 0.5*contact.m1m2.height + self.gate_contact_yoffset + + # y-position of inverter nmos + self.inverter_nmos_ypos = self.port_ypos + + # spacing between ports (same for read/write and write ports) + self.bitline_offset = -0.5*self.readwrite_nmos.active_width + 0.5*contact.m1m2.height + self.m2_space + self.m2_width + m2_constraint = self.bitline_offset + self.m2_space + 0.5*contact.m1m2.height - 0.5*self.readwrite_nmos.active_width + self.write_port_spacing = max(self.active_space, self.m1_space, m2_constraint) + self.read_port_spacing = self.bitline_offset + self.m2_space + + # spacing between cross coupled inverters + self.inverter_to_inverter_spacing = contact.poly.height + self.m1_space + + # calculations related to inverter connections + inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height) + inverter_nmos_contact_extension = 0.5*(self.inverter_nmos.active_contact.height - self.inverter_nmos.active_height) + self.inverter_gap = max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + + self.poly_to_polycontact + 2*contact.poly.width \ + + self.m1_space + inverter_pmos_contact_extension + self.cross_couple_lower_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ + + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + + 0.5*contact.poly.width + self.cross_couple_upper_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ + + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + + self.poly_to_polycontact \ + + 1.5*contact.poly.width + + # spacing between wordlines (and gnd) + self.rowline_spacing = self.m1_space + contact.m1m2.width + self.rowline_offset = -0.5*self.m1_width + + # spacing for vdd + implant_constraint = max(inverter_pmos_contact_extension, 0) + 2*self.implant_enclose_active + 0.5*(contact.well.width - self.m1_width) + metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space + self.vdd_offset = max(implant_constraint, metal1_constraint) + 0.5*self.m1_width + + # read port dimensions + width_reduction = self.read_nmos.active_width - self.read_nmos.get_pin("D").cx() + self.read_port_width = 2*self.read_nmos.active_width - 2*width_reduction + + def calculate_postions(self): + """ Calculate positions that describe the edges and dimensions of the cell """ + self.botmost_ypos = -0.5*self.m1_width - self.total_ports*self.rowline_spacing + self.topmost_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset + + self.leftmost_xpos = -0.5*self.inverter_to_inverter_spacing - self.inverter_nmos.active_width \ + - self.num_rw_ports*(self.readwrite_nmos.active_width + self.write_port_spacing) \ + - self.num_w_ports*(self.write_nmos.active_width + self.write_port_spacing) \ + - self.num_r_ports*(self.read_port_width + self.read_port_spacing) \ + - self.bitline_offset - 0.5*contact.m1m2.width + + self.width = -2*self.leftmost_xpos + self.height = self.topmost_ypos - self.botmost_ypos + + self.center_ypos = 0.5*(self.topmost_ypos + self.botmost_ypos) + + def create_storage(self): + """ + Creates the crossed coupled inverters that act as storage for the bitcell. + The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar". + """ + # create active for nmos + self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", + mod=self.inverter_nmos) + self.connect_inst(["Q", self.Q_bar, "gnd", "gnd"]) + + self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right", + mod=self.inverter_nmos) + self.connect_inst(["gnd", "Q", self.Q_bar, "gnd"]) + + # create active for pmos + self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left", + mod=self.inverter_pmos) + self.connect_inst(["Q", self.Q_bar, "vdd", "vdd"]) + + self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right", + mod=self.inverter_pmos) + self.connect_inst(["vdd", "Q", self.Q_bar, "vdd"]) + + def place_storage(self): + """ Places the transistors for the crossed coupled inverters in the bitcell """ + # calculate transistor offsets + left_inverter_xpos = -0.5*self.inverter_to_inverter_spacing - self.inverter_nmos.active_width + right_inverter_xpos = 0.5*self.inverter_to_inverter_spacing + inverter_pmos_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + + # create active for nmos + self.inverter_nmos_left.place([left_inverter_xpos, self.inverter_nmos_ypos]) + self.inverter_nmos_right.place([right_inverter_xpos, self.inverter_nmos_ypos]) + + # create active for pmos + self.inverter_pmos_left.place([left_inverter_xpos, inverter_pmos_ypos]) + self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos]) + + # update furthest left and right transistor edges (this will propagate to further transistor offset calculations) + self.left_building_edge = left_inverter_xpos + self.right_building_edge = right_inverter_xpos + self.inverter_nmos.active_width + + def route_storage(self): + """ Routes inputs and outputs of inverters to cross couple them """ + # connect input (gate) of inverters + self.add_path("poly", [self.inverter_nmos_left.get_pin("G").uc(), self.inverter_pmos_left.get_pin("G").bc()]) + self.add_path("poly", [self.inverter_nmos_right.get_pin("G").uc(), self.inverter_pmos_right.get_pin("G").bc()]) + + # connect output (drain/source) of inverters + self.add_path("metal1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], width=contact.well.second_layer_width) + self.add_path("metal1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], width=contact.well.second_layer_width) + + # add contacts to connect gate poly to drain/source metal1 (to connect Q to Q_bar) + contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x + 0.5*contact.poly.height, self.cross_couple_upper_ypos) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=contact_offset_left, + rotate=90) + + contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.height, self.cross_couple_lower_ypos) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=contact_offset_right, + rotate=90) + + # connect contacts to gate poly (cross couple connections) + gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x, contact_offset_left.y) + self.add_path("poly", [contact_offset_left, gate_offset_right]) + + gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").rc().x, contact_offset_right.y) + self.add_path("poly", [contact_offset_right, gate_offset_left]) + + def route_rails(self): + """ Adds gnd and vdd rails and connects them to the inverters """ + # Add rails for vdd and gnd + gnd_ypos = self.rowline_offset - self.total_ports*self.rowline_spacing + self.gnd_position = vector(0, gnd_ypos) + self.add_rect_center(layer="metal1", + offset=self.gnd_position, + width=self.width, + height=self.m1_width) + self.add_power_pin("gnd", vector(0,gnd_ypos)) + + + vdd_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset + self.vdd_position = vector(0, vdd_ypos) + self.add_rect_center(layer="metal1", + offset=self.vdd_position, + width=self.width, + height=self.m1_width) + self.add_power_pin("vdd", vector(0,vdd_ypos)) + + def create_readwrite_ports(self): + """ + Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell. + A read or write is enabled by setting a Read-Write-Wordline (RWWL) high, subsequently turning on the transistor. + The transistor is connected between a Read-Write-Bitline (RWBL) and the storage component of the cell (Q). + In a write operation, driving RWBL high or low sets the value of the cell. + In a read operation, RWBL is precharged, then is either remains high or is discharged depending on the value of the cell. + This is a differential design, so each write port has a mirrored port that connects RWBR to Q_bar. + """ + # define read/write transistor variables as empty arrays based on the number of read/write ports + self.readwrite_nmos_left = [None] * self.num_rw_ports + self.readwrite_nmos_right = [None] * self.num_rw_ports + + # iterate over the number of read/write ports + for k in range(0,self.num_rw_ports): + # add read/write transistors + self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k), + mod=self.readwrite_nmos) + self.connect_inst([self.rw_bl_names[k], self.rw_wl_names[k], "Q", "gnd"]) + + self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k), + mod=self.readwrite_nmos) + self.connect_inst([self.Q_bar, self.rw_wl_names[k], self.rw_br_names[k], "gnd"]) + + def place_readwrite_ports(self): + """ Places read/write ports in the bit cell """ + # define read/write transistor variables as empty arrays based on the number of read/write ports + self.rwwl_positions = [None] * self.num_rw_ports + self.rwbl_positions = [None] * self.num_rw_ports + self.rwbr_positions = [None] * self.num_rw_ports + + # iterate over the number of read/write ports + for k in range(0,self.num_rw_ports): + # calculate read/write transistor offsets + left_readwrite_transistor_xpos = self.left_building_edge \ + - (k+1)*self.write_port_spacing \ + - (k+1)*self.readwrite_nmos.active_width + + right_readwrite_transistor_xpos = self.right_building_edge \ + + (k+1)*self.write_port_spacing \ + + k*self.readwrite_nmos.active_width + + # place read/write transistors + self.readwrite_nmos_left[k].place(offset=[left_readwrite_transistor_xpos, self.port_ypos]) + + self.readwrite_nmos_right[k].place(offset=[right_readwrite_transistor_xpos, self.port_ypos]) + + # add pin for RWWL + rwwl_ypos = self.rowline_offset - k*self.rowline_spacing + self.rwwl_positions[k] = vector(0, rwwl_ypos) + self.add_layout_pin_rect_center(text=self.rw_wl_names[k], + layer="metal1", + offset=self.rwwl_positions[k], + width=self.width, + height=self.m1_width) + + # add pins for RWBL and RWBR + rwbl_xpos = left_readwrite_transistor_xpos - self.bitline_offset + 0.5*self.m2_width + self.rwbl_positions[k] = vector(rwbl_xpos, self.center_ypos) + self.add_layout_pin_rect_center(text=self.rw_bl_names[k], + layer="metal2", + offset=self.rwbl_positions[k], + width=drc["minwidth_metal2"], + height=self.height) + + rwbr_xpos = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width + self.bitline_offset - 0.5*self.m2_width + self.rwbr_positions[k] = vector(rwbr_xpos, self.center_ypos) + self.add_layout_pin_rect_center(text=self.rw_br_names[k], + layer="metal2", + offset=self.rwbr_positions[k], + width=drc["minwidth_metal2"], + height=self.height) + + # update furthest left and right transistor edges + self.left_building_edge = left_readwrite_transistor_xpos + self.right_building_edge = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width + + def create_write_ports(self): + """ + Creates write ports in the bit cell. A differential pair of transistors can write only. + A write is enabled by setting a Write-Rowline (WWL) high, subsequently turning on the transistor. + The transistor is connected between a Write-Bitline (WBL) and the storage component of the cell (Q). + In a write operation, driving WBL high or low sets the value of the cell. + This is a differential design, so each write port has a mirrored port that connects WBR to Q_bar. + """ + # define write transistor variables as empty arrays based on the number of write ports + self.write_nmos_left = [None] * self.num_w_ports + self.write_nmos_right = [None] * self.num_w_ports + + # iterate over the number of write ports + for k in range(0,self.num_w_ports): + # add write transistors + self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k), + mod=self.write_nmos) + self.connect_inst([self.w_bl_names[k], self.w_wl_names[k], "Q", "gnd"]) + + self.write_nmos_right[k] = self.add_inst(name="write_nmos_right{}".format(k), + mod=self.write_nmos) + self.connect_inst([self.Q_bar, self.w_wl_names[k], self.w_br_names[k], "gnd"]) + + def place_write_ports(self): + """ Places write ports in the bit cell """ + # define write transistor variables as empty arrays based on the number of write ports + self.wwl_positions = [None] * self.num_w_ports + self.wbl_positions = [None] * self.num_w_ports + self.wbr_positions = [None] * self.num_w_ports + + # iterate over the number of write ports + for k in range(0,self.num_w_ports): + # Add transistors + # calculate write transistor offsets + left_write_transistor_xpos = self.left_building_edge \ + - (k+1)*self.write_port_spacing \ + - (k+1)*self.write_nmos.active_width + + right_write_transistor_xpos = self.right_building_edge \ + + (k+1)*self.write_port_spacing \ + + k*self.write_nmos.active_width + + # add write transistors + self.write_nmos_left[k].place(offset=[left_write_transistor_xpos, self.port_ypos]) + + self.write_nmos_right[k].place(offset=[right_write_transistor_xpos, self.port_ypos]) + + # add pin for WWL + wwl_ypos = rwwl_ypos = self.rowline_offset - self.num_rw_ports*self.rowline_spacing - k*self.rowline_spacing + self.wwl_positions[k] = vector(0, wwl_ypos) + self.add_layout_pin_rect_center(text=self.w_wl_names[k], + layer="metal1", + offset=self.wwl_positions[k], + width=self.width, + height=self.m1_width) + + # add pins for WBL and WBR + wbl_xpos = left_write_transistor_xpos - self.bitline_offset + 0.5*self.m2_width + self.wbl_positions[k] = vector(wbl_xpos, self.center_ypos) + self.add_layout_pin_rect_center(text=self.w_bl_names[k], + layer="metal2", + offset=self.wbl_positions[k], + width=drc["minwidth_metal2"], + height=self.height) + + wbr_xpos = right_write_transistor_xpos + self.write_nmos.active_width + self.bitline_offset - 0.5*self.m2_width + self.wbr_positions[k] = vector(wbr_xpos, self.center_ypos) + self.add_layout_pin_rect_center(text=self.w_br_names[k], + layer="metal2", + offset=self.wbr_positions[k], + width=drc["minwidth_metal2"], + height=self.height) + + # update furthest left and right transistor edges + self.left_building_edge = left_write_transistor_xpos + self.right_building_edge = right_write_transistor_xpos + self.write_nmos.active_width + + def create_read_ports(self): + """ + Creates read ports in the bit cell. A differential pair of ports can read only. + Two transistors function as a read port, denoted as the "read transistor" and the "read-access transistor". + The read transistor is connected to RWL (gate), RBL (drain), and the read-access transistor (source). + The read-access transistor is connected to Q_bar (gate), gnd (source), and the read transistor (drain). + A read is enabled by setting a Read-Rowline (RWL) high, subsequently turning on the read transistor. + The Read-Bitline (RBL) is precharged to high, and when the value of Q_bar is high, the read-access transistor + is turned on, creating a connection between RBL and gnd. RBL subsequently discharges allowing for a differential read + using sense amps. This is a differential design, so each read port has a mirrored port that connects RBL_bar to Q. + """ + + # define read transistor variables as empty arrays based on the number of read ports + self.read_nmos_left = [None] * self.num_r_ports + self.read_nmos_right = [None] * self.num_r_ports + self.read_access_nmos_left = [None] * self.num_r_ports + self.read_access_nmos_right = [None] * self.num_r_ports + + # iterate over the number of read ports + for k in range(0,self.num_r_ports): + # add read-access transistors + self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k), + mod=self.read_nmos) + self.connect_inst(["RA_to_R_left{}".format(k), self.Q_bar, "gnd", "gnd"]) + + self.read_access_nmos_right[k] = self.add_inst(name="read_access_nmos_right{}".format(k), + mod=self.read_nmos) + self.connect_inst(["gnd", "Q", "RA_to_R_right{}".format(k), "gnd"]) + + # add read transistors + self.read_nmos_left[k] = self.add_inst(name="read_nmos_left{}".format(k), + mod=self.read_nmos) + self.connect_inst([self.r_bl_names[k], self.r_wl_names[k], "RA_to_R_left{}".format(k), "gnd"]) + + self.read_nmos_right[k] = self.add_inst(name="read_nmos_right{}".format(k), + mod=self.read_nmos) + self.connect_inst(["RA_to_R_right{}".format(k), self.r_wl_names[k], self.r_br_names[k], "gnd"]) + + def place_read_ports(self): + """ Places the read ports in the bit cell """ + # define read transistor variables as empty arrays based on the number of read ports + self.rwl_positions = [None] * self.num_r_ports + self.rbl_positions = [None] * self.num_r_ports + self.rbr_positions = [None] * self.num_r_ports + + # calculate offset to overlap the drain of the read-access transistor with the source of the read transistor + overlap_offset = self.read_nmos.get_pin("D").cx() - self.read_nmos.get_pin("S").cx() + + # iterate over the number of read ports + for k in range(0,self.num_r_ports): + # calculate transistor offsets + left_read_transistor_xpos = self.left_building_edge \ + - (k+1)*self.read_port_spacing \ + - (k+1)*self.read_port_width + + right_read_transistor_xpos = self.right_building_edge \ + + (k+1)*self.read_port_spacing \ + + k*self.read_port_width + + # add read-access transistors + self.read_access_nmos_left[k].place(offset=[left_read_transistor_xpos+overlap_offset, self.port_ypos]) + + self.read_access_nmos_right[k].place(offset=[right_read_transistor_xpos, self.port_ypos]) + + # add read transistors + self.read_nmos_left[k].place(offset=[left_read_transistor_xpos, self.port_ypos]) + + self.read_nmos_right[k].place(offset=[right_read_transistor_xpos+overlap_offset, self.port_ypos]) + + # add pin for RWL + rwl_ypos = rwwl_ypos = self.rowline_offset - self.num_rw_ports*self.rowline_spacing - self.num_w_ports*self.rowline_spacing - k*self.rowline_spacing + self.rwl_positions[k] = vector(0, rwl_ypos) + self.add_layout_pin_rect_center(text=self.r_wl_names[k], + layer="metal1", + offset=self.rwl_positions[k], + width=self.width, + height=self.m1_width) + + # add pins for RBL and RBR + rbl_xpos = left_read_transistor_xpos - self.bitline_offset + 0.5*self.m2_width + self.rbl_positions[k] = vector(rbl_xpos, self.center_ypos) + self.add_layout_pin_rect_center(text=self.r_bl_names[k], + layer="metal2", + offset=self.rbl_positions[k], + width=drc["minwidth_metal2"], + height=self.height) + + rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset - 0.5*self.m2_width + self.rbr_positions[k] = vector(rbr_xpos, self.center_ypos) + self.add_layout_pin_rect_center(text=self.r_br_names[k], + layer="metal2", + offset=self.rbr_positions[k], + width=drc["minwidth_metal2"], + height=self.height) + + def route_wordlines(self): + """ Routes gate of transistors to their respective wordlines """ + port_transistors = [] + for k in range(self.num_rw_ports): + port_transistors.append(self.readwrite_nmos_left[k]) + port_transistors.append(self.readwrite_nmos_right[k]) + for k in range(self.num_w_ports): + port_transistors.append(self.write_nmos_left[k]) + port_transistors.append(self.write_nmos_right[k]) + for k in range(self.num_r_ports): + port_transistors.append(self.read_nmos_left[k]) + port_transistors.append(self.read_nmos_right[k]) + + wl_positions = [] + for k in range(self.num_rw_ports): + wl_positions.append(self.rwwl_positions[k]) + wl_positions.append(self.rwwl_positions[k]) + for k in range(self.num_w_ports): + wl_positions.append(self.wwl_positions[k]) + wl_positions.append(self.wwl_positions[k]) + for k in range(self.num_r_ports): + wl_positions.append(self.rwl_positions[k]) + wl_positions.append(self.rwl_positions[k]) + + for k in range(2*self.total_ports): + gate_offset = port_transistors[k].get_pin("G").bc() + port_contact_offset = gate_offset + vector(0, -self.gate_contact_yoffset + self.poly_extend_active) + wl_contact_offset = vector(gate_offset.x, wl_positions[k].y) + + # first transistor on either side of the cross coupled inverters does not need to route to wordline on metal2 + if (k == 0) or (k == 1): + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=port_contact_offset) + + self.add_path("poly", [gate_offset, port_contact_offset]) + self.add_path("metal1", [port_contact_offset, wl_contact_offset]) + + else: + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=port_contact_offset) + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=port_contact_offset) + + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=wl_contact_offset, + rotate=90) + + self.add_path("poly", [gate_offset, port_contact_offset]) + self.add_path("metal2", [port_contact_offset, wl_contact_offset]) + + def route_bitlines(self): + """ Routes read/write transistors to their respective bitlines """ + left_port_transistors = [] + right_port_transistors = [] + for k in range(self.num_rw_ports): + left_port_transistors.append(self.readwrite_nmos_left[k]) + right_port_transistors.append(self.readwrite_nmos_right[k]) + for k in range(self.num_w_ports): + left_port_transistors.append(self.write_nmos_left[k]) + right_port_transistors.append(self.write_nmos_right[k]) + for k in range(self.num_r_ports): + left_port_transistors.append(self.read_nmos_left[k]) + right_port_transistors.append(self.read_nmos_right[k]) + + bl_positions = [] + br_positions = [] + for k in range(self.num_rw_ports): + bl_positions.append(self.rwbl_positions[k]) + br_positions.append(self.rwbr_positions[k]) + for k in range(self.num_w_ports): + bl_positions.append(self.wbl_positions[k]) + br_positions.append(self.wbr_positions[k]) + for k in range(self.num_r_ports): + bl_positions.append(self.rbl_positions[k]) + br_positions.append(self.rbr_positions[k]) + + for k in range(self.total_ports): + port_contact_offest = left_port_transistors[k].get_pin("S").center() + bl_offset = vector(bl_positions[k].x, port_contact_offest.y) + + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=port_contact_offest) + + self.add_path("metal2", [port_contact_offest, bl_offset], width=contact.m1m2.height) + + for k in range(self.total_ports): + port_contact_offest = right_port_transistors[k].get_pin("D").center() + br_offset = vector(br_positions[k].x, port_contact_offest.y) + + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=port_contact_offest) + + self.add_path("metal2", [port_contact_offest, br_offset], width=contact.m1m2.height) + + def route_supply(self): + """ Route inverter nmos and read-access nmos to gnd. Route inverter pmos to vdd. """ + # route inverter nmos and read-access nmos to gnd + nmos_contact_positions = [] + nmos_contact_positions.append(self.inverter_nmos_left.get_pin("S").center()) + nmos_contact_positions.append(self.inverter_nmos_right.get_pin("D").center()) + for k in range(self.num_r_ports): + nmos_contact_positions.append(self.read_access_nmos_left[k].get_pin("D").center()) + nmos_contact_positions.append(self.read_access_nmos_right[k].get_pin("S").center()) + + for position in nmos_contact_positions: + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=position) + + if position.x > 0: + contact_correct = 0.5*contact.m1m2.height + else: + contact_correct = -0.5*contact.m1m2.height + supply_offset = vector(position.x + contact_correct, self.gnd_position.y) + self.add_contact_center(layers=("metal1", "via1", "metal2"), + offset=supply_offset, + rotate=90) + + self.add_path("metal2", [position, supply_offset]) + + # route inverter pmos to vdd + vdd_pos_left = vector(self.inverter_nmos_left.get_pin("S").uc().x, self.vdd_position.y) + self.add_path("metal1", [self.inverter_pmos_left.get_pin("S").uc(), vdd_pos_left]) + + vdd_pos_right = vector(self.inverter_nmos_right.get_pin("D").uc().x, self.vdd_position.y) + self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) + + def route_readwrite_access(self): + """ Routes read/write transistors to the storage component of the bitcell """ + for k in range(self.num_rw_ports): + mid = vector(self.readwrite_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) + Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) + self.add_path("metal1", [self.readwrite_nmos_left[k].get_pin("D").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) + self.add_path("metal1", [mid, Q_pos]) + + mid = vector(self.readwrite_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) + Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) + self.add_path("metal1", [self.readwrite_nmos_right[k].get_pin("S").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) + self.add_path("metal1", [mid, Q_bar_pos]) + + def route_write_access(self): + """ Routes read/write transistors to the storage component of the bitcell """ + for k in range(self.num_w_ports): + mid = vector(self.write_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) + Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) + self.add_path("metal1", [self.write_nmos_left[k].get_pin("D").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) + self.add_path("metal1", [mid, Q_pos]) + + mid = vector(self.write_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) + Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) + self.add_path("metal1", [self.write_nmos_right[k].get_pin("S").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) + self.add_path("metal1", [mid, Q_bar_pos]) + + def route_read_access(self): + """ Routes read access transistors to the storage component of the bitcell """ + # add poly to metal1 contacts for gates of the inverters + left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_upper_ypos) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=left_storage_contact, + rotate=90) + + right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_upper_ypos) + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=right_storage_contact, + rotate=90) + + inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos) + self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) + + inverter_gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_upper_ypos) + self.add_path("poly", [right_storage_contact, inverter_gate_offset_right]) + + # add poly to metal1 contacts for gates of read-access transistors + # route from read-access contacts to inverter contacts on metal1 + for k in range(self.num_r_ports): + port_contact_offset = self.read_access_nmos_left[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active) + + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=port_contact_offset) + + self.add_path("poly", [self.read_access_nmos_left[k].get_pin("G").uc(), port_contact_offset]) + + mid = vector(self.read_access_nmos_left[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) + self.add_path("metal1", [port_contact_offset, mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) + self.add_path("metal1", [mid, left_storage_contact]) + + port_contact_offset = self.read_access_nmos_right[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active) + + self.add_contact_center(layers=("poly", "contact", "metal1"), + offset=port_contact_offset) + + self.add_path("poly", [self.read_access_nmos_right[k].get_pin("G").uc(), port_contact_offset]) + + mid = vector(self.read_access_nmos_right[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) + self.add_path("metal1", [port_contact_offset, mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) + self.add_path("metal1", [mid, right_storage_contact]) + + def extend_well(self): + """ + Connects wells between ptx modules and places well contacts""" + # extend pwell to encompass entire nmos region of the cell up to the height of the tallest nmos transistor + max_nmos_well_height = max(self.inverter_nmos.cell_well_height, + self.readwrite_nmos.cell_well_height, + self.write_nmos.cell_well_height, + self.read_nmos.cell_well_height) + well_height = max_nmos_well_height + self.port_ypos - self.well_enclose_active - self.gnd_position.y + offset = vector(self.leftmost_xpos, self.botmost_ypos) + self.add_rect(layer="pwell", + offset=offset, + width=self.width, + height=well_height) + + # extend nwell to encompass inverter_pmos + # calculate offset of the left pmos well + inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) - drc["well_enclosure_active"] + inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap - drc["well_enclosure_active"] + + # calculate width of the two combined nwells + # calculate height to encompass nimplant connected to vdd + well_width = 2*(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) + 2*drc["well_enclosure_active"] + well_height = self.vdd_position.y - inverter_well_ypos + drc["well_enclosure_active"] + drc["minwidth_tx"] + + offset = [inverter_well_xpos,inverter_well_ypos] + self.add_rect(layer="nwell", + offset=offset, + width=well_width, + height=well_height) + + # add well contacts + # connect pimplants to gnd + offset = vector(0, self.gnd_position.y) + self.add_contact_center(layers=("active", "contact", "metal1"), + offset=offset, + rotate=90, + implant_type="p", + well_type="p") + + # connect nimplants to vdd + offset = vector(0, self.vdd_position.y) + self.add_contact_center(layers=("active", "contact", "metal1"), + offset=offset, + rotate=90, + implant_type="n", + well_type="n") + + def list_bitcell_pins(self, col, row): + """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ + bitcell_pins = [] + for port in range(self.total_ports): + bitcell_pins.append("bl{0}_{1}".format(port,col)) + bitcell_pins.append("br{0}_{1}".format(port,col)) + for port in range(self.total_ports): + bitcell_pins.append("wl{0}_{1}".format(port,row)) + bitcell_pins.append("vdd") + bitcell_pins.append("gnd") + return bitcell_pins + + def list_all_wl_names(self): + """ Creates a list of all wordline pin names """ + wordline_names = self.rw_wl_names + self.w_wl_names + self.r_wl_names + return wordline_names + + def list_all_bitline_names(self): + """ Creates a list of all bitline pin names (both bl and br) """ + bitline_pins = [] + for port in range(self.total_ports): + bitline_pins.append("bl{0}".format(port)) + bitline_pins.append("br{0}".format(port)) + return bitline_pins + + def list_all_bl_names(self): + """ Creates a list of all bl pins names """ + bl_pins = self.rw_bl_names + self.w_bl_names + self.r_bl_names + return bl_pins + + def list_all_br_names(self): + """ Creates a list of all br pins names """ + br_pins = self.rw_br_names + self.w_br_names + self.r_br_names + return br_pins + + def route_rbc_short(self): + """ route the short from Q_bar to gnd necessary for the replica bitcell """ + Q_bar_pos = self.inverter_pmos_right.get_pin("S").center() + vdd_pos = self.inverter_pmos_right.get_pin("D").center() + self.add_path("metal1", [Q_bar_pos, vdd_pos]) + + def analytical_delay(self, slew, load=0, swing = 0.5): + #FIXME: Delay copied exactly over from bitcell + from tech import spice + r = spice["min_tx_r"]*3 + c_para = spice["min_tx_drain_c"] + result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew, swing = swing) + return result + + def analytical_power(self, proc, vdd, temp, load): + """Bitcell power in nW. Only characterizes leakage.""" + from tech import spice + leakage = spice["bitcell_leakage"] + dynamic = 0 #temporary + total_power = self.return_power(dynamic, leakage) + return total_power + + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #pbitcell uses the different sizing for the port access tx's. Not accounted for in this model. + access_tx_cin = self.readwrite_nmos.get_cin() + return 2*access_tx_cin + diff --git a/compiler/modules/replica_bitcell.py b/compiler/bitcells/replica_bitcell.py similarity index 61% rename from compiler/modules/replica_bitcell.py rename to compiler/bitcells/replica_bitcell.py index 7bbdbe06..d896e29e 100644 --- a/compiler/modules/replica_bitcell.py +++ b/compiler/bitcells/replica_bitcell.py @@ -1,7 +1,7 @@ import design import debug import utils -from tech import GDS,layer +from tech import GDS,layer,drc,parameter class replica_bitcell(design.design): """ @@ -12,7 +12,7 @@ class replica_bitcell(design.design): pin_names = ["bl", "br", "wl", "vdd", "gnd"] (width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"]) def __init__(self): design.design.__init__(self, "replica_cell_6t") @@ -21,3 +21,10 @@ class replica_bitcell(design.design): self.width = replica_bitcell.width self.height = replica_bitcell.height self.pin_map = replica_bitcell.pin_map + + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #This is a handmade cell so the value must be entered in the tech.py file or estimated. + #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. + access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] + return 2*access_tx_cin \ No newline at end of file diff --git a/compiler/bitcells/replica_bitcell_1rw_1r.py b/compiler/bitcells/replica_bitcell_1rw_1r.py new file mode 100644 index 00000000..f5151958 --- /dev/null +++ b/compiler/bitcells/replica_bitcell_1rw_1r.py @@ -0,0 +1,31 @@ +import design +import debug +import utils +from tech import GDS,layer,drc,parameter + +class replica_bitcell_1rw_1r(design.design): + """ + A single bit cell which is forced to store a 0. + This module implements the single memory cell used in the design. It + is a hand-made cell, so the layout and netlist should be available in + the technology library. """ + + pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + (width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"]) + + def __init__(self): + design.design.__init__(self, "replica_cell_1rw_1r") + debug.info(2, "Create replica bitcell 1rw+1r object") + + self.width = replica_bitcell_1rw_1r.width + self.height = replica_bitcell_1rw_1r.height + self.pin_map = replica_bitcell_1rw_1r.pin_map + + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #This is a handmade cell so the value must be entered in the tech.py file or estimated. + #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. + #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] + return 2*access_tx_cin \ No newline at end of file diff --git a/compiler/bitcells/replica_pbitcell.py b/compiler/bitcells/replica_pbitcell.py new file mode 100644 index 00000000..666d3646 --- /dev/null +++ b/compiler/bitcells/replica_pbitcell.py @@ -0,0 +1,86 @@ +import debug +import design +from tech import drc, spice,parameter +from vector import vector +from globals import OPTS +from pbitcell import pbitcell + +class replica_pbitcell(design.design): + """ + Creates a replica bitcell using pbitcell + """ + + def __init__(self): + + self.num_rw_ports = OPTS.num_rw_ports + self.num_w_ports = OPTS.num_w_ports + self.num_r_ports = OPTS.num_r_ports + self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports + + design.design.__init__(self, "replica_pbitcell") + debug.info(1, "create a replica bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, + self.num_w_ports, + self.num_r_ports)) + + self.create_netlist() + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_modules() + + def create_layout(self): + self.place_pbitcell() + self.route_rbc_connections() + self.DRC_LVS() + + def add_pins(self): + for port in range(self.total_ports): + self.add_pin("bl{}".format(port)) + self.add_pin("br{}".format(port)) + + for port in range(self.total_ports): + self.add_pin("wl{}".format(port)) + + self.add_pin("vdd") + self.add_pin("gnd") + + def add_modules(self): + self.prbc = pbitcell(replica_bitcell=True) + self.add_mod(self.prbc) + + self.height = self.prbc.height + self.width = self.prbc.width + + def create_modules(self): + self.prbc_inst = self.add_inst(name="pbitcell", + mod=self.prbc) + + temp = [] + for port in range(self.total_ports): + temp.append("bl{}".format(port)) + temp.append("br{}".format(port)) + for port in range(self.total_ports): + temp.append("wl{}".format(port)) + temp.append("vdd") + temp.append("gnd") + self.connect_inst(temp) + + def place_pbitcell(self): + offset = [0,0] + self.prbc_inst.place(offset=offset) + + def route_rbc_connections(self): + for port in range(self.total_ports): + self.copy_layout_pin(self.prbc_inst, "bl{}".format(port)) + self.copy_layout_pin(self.prbc_inst, "br{}".format(port)) + for port in range(self.total_ports): + self.copy_layout_pin(self.prbc_inst, "wl{}".format(port)) + self.copy_layout_pin(self.prbc_inst, "vdd") + self.copy_layout_pin(self.prbc_inst, "gnd") + + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #This module is made using a pbitcell. Get the cin from that module + return self.prbc.get_wl_cin() \ No newline at end of file diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index dc9398d0..19da7d59 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -5,7 +5,10 @@ from globals import OPTS,find_exe,get_tool from .lib import * from .delay import * from .setup_hold import * - +from .functional import * +from .worst_case import * +from .simulation import * +from .bitline_delay import * debug.info(1,"Initializing characterizer...") OPTS.spice_exe = "" @@ -15,10 +18,10 @@ if not OPTS.analytical_delay: if OPTS.spice_name != "": OPTS.spice_exe=find_exe(OPTS.spice_name) - if OPTS.spice_exe=="": + if OPTS.spice_exe=="" or OPTS.spice_exe==None: debug.error("{0} not found. Unable to perform characterization.".format(OPTS.spice_name),1) else: - (OPTS.spice_name,OPTS.spice_exe) = get_tool("spice",["xa", "hspice", "ngspice", "ngspice.exe"]) + (OPTS.spice_name,OPTS.spice_exe) = get_tool("spice",["hspice", "ngspice", "ngspice.exe", "xa"]) # set the input dir for spice files if using ngspice if OPTS.spice_name == "ngspice": diff --git a/compiler/characterizer/bitline_delay.py b/compiler/characterizer/bitline_delay.py new file mode 100644 index 00000000..e5a50c16 --- /dev/null +++ b/compiler/characterizer/bitline_delay.py @@ -0,0 +1,150 @@ +import sys,re,shutil +import debug +import tech +import math +from .stimuli import * +from .trim_spice import * +from .charutils import * +import utils +from globals import OPTS +from .delay import delay + +class bitline_delay(delay): + """Functions to test for the worst case delay in a target SRAM + + The current worst case determines a feasible period for the SRAM then tests + several bits and record the delay and differences between the bits. + + """ + + def __init__(self, sram, spfile, corner): + delay.__init__(self,sram,spfile,corner) + self.period = tech.spice["feasible_period"] + self.is_bitline_measure = True + + def create_measurement_names(self): + """Create measurement names. The names themselves currently define the type of measurement""" + #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. + self.bitline_meas_names = ["bl_volt", "br_volt"] + + def write_delay_measures(self): + """ + Write the measure statements to quantify the bitline voltage at sense amp enable 50%. + """ + self.sf.write("\n* Measure statements for delay and power\n") + + # Output some comments to aid where cycles start and + for comment in self.cycle_comments: + self.sf.write("* {}\n".format(comment)) + + for read_port in self.targ_read_ports: + self.write_bitline_measures_read_port(read_port) + + def write_bitline_measures_read_port(self, port): + """ + Write the measure statements to quantify the delay and power results for a read port. + """ + # add measure statements for delays/slews + measure_bitline = self.get_data_bit_column_number(self.probe_address, self.probe_data) + debug.info(2, "Measuring bitline column={}".format(measure_bitline)) + for port in self.targ_read_ports: + if len(self.all_ports) == 1: #special naming case for single port sram bitlines + bitline_port = "" + else: + bitline_port = str(port) + + sen_name = "Xsram.s_en{}".format(port) + bl_name = "Xsram.Xbank0.bl{}_{}".format(bitline_port, measure_bitline) + br_name = "Xsram.Xbank0.br{}_{}".format(bitline_port, measure_bitline) + self.stim.gen_meas_find_voltage("bl_volt", sen_name, bl_name, .5, "RISE", self.cycle_times[self.measure_cycles[port]["read0"]]) + self.stim.gen_meas_find_voltage("br_volt", sen_name, br_name, .5, "RISE", self.cycle_times[self.measure_cycles[port]["read0"]]) + + def gen_test_cycles_one_port(self, read_port, write_port): + """Sets a list of key time-points [ns] of the waveform (each rising edge) + of the cycles to do a timing evaluation of a single port """ + + # Create the inverse address for a scratch address + inverse_address = self.calculate_inverse_address() + + # For now, ignore data patterns and write ones or zeros + data_ones = "1"*self.word_size + data_zeros = "0"*self.word_size + + if self.t_current == 0: + self.add_noop_all_ports("Idle cycle (no positive clock edge)", + inverse_address, data_zeros) + + self.add_write("W data 1 address {}".format(inverse_address), + inverse_address,data_ones,write_port) + + self.add_write("W data 0 address {} to write value".format(self.probe_address), + self.probe_address,data_zeros,write_port) + self.measure_cycles[write_port]["write0"] = len(self.cycle_times)-1 + + # This also ensures we will have a H->L transition on the next read + self.add_read("R data 1 address {} to set DOUT caps".format(inverse_address), + inverse_address,data_zeros,read_port) + self.measure_cycles[read_port]["read1"] = len(self.cycle_times)-1 + + self.add_read("R data 0 address {} to check W0 worked".format(self.probe_address), + self.probe_address,data_zeros,read_port) + self.measure_cycles[read_port]["read0"] = len(self.cycle_times)-1 + def get_data_bit_column_number(self, probe_address, probe_data): + """Calculates bitline column number of data bit under test using bit position and mux size""" + if self.sram.col_addr_size>0: + col_address = int(probe_address[0:self.sram.col_addr_size],2) + else: + col_address = 0 + bl_column = int(self.sram.words_per_row*probe_data + col_address) + return bl_column + + def run_delay_simulation(self): + """ + This tries to simulate a period and checks if the result works. If + so, it returns True and the delays, slews, and powers. It + works on the trimmed netlist by default, so powers do not + include leakage of all cells. + """ + #Sanity Check + debug.check(self.period > 0, "Target simulation period non-positive") + + result = [{} for i in self.all_ports] + # Checking from not data_value to data_value + self.write_delay_stimulus() + + self.stim.run_sim() #running sim prodoces spice output file. + + for port in self.targ_read_ports: + bitlines_meas_vals = {} + for mname in self.bitline_meas_names: + bitlines_meas_vals[mname] = parse_spice_list("timing", mname) + #Check that power parsing worked. + for name, val in bitlines_meas_vals.items(): + if type(val)!=float: + debug.error("Failed to Parse Bitline Values:\n\t\t{0}".format(bitlines_meas_vals),1) #Printing the entire dict looks bad. + result[port].update(bitlines_meas_vals) + + + # The delay is from the negative edge for our SRAM + return (True,result) + + def analyze(self, probe_address, probe_data, slews, loads): + """Measures the bitline swing of the differential bitlines (bl/br) at 50% s_en """ + self.set_probe(probe_address, probe_data) + self.load=max(loads) + self.slew=max(slews) + + read_port = self.read_ports[0] #only test the first read port + bitline_swings = {} + self.targ_read_ports = [read_port] + self.targ_write_ports = [self.write_ports[0]] + debug.info(1,"Bitline swing test: corner {}".format(self.corner)) + (success, results)=self.run_delay_simulation() + debug.check(success, "Bitline Failed: period {}".format(self.period)) + for mname in self.bitline_meas_names: + bitline_swings[mname] = results[read_port][mname] + debug.info(1,"Bitline values (bl/br): {}".format(bitline_swings)) + return bitline_swings + + + diff --git a/compiler/characterizer/charutils.py b/compiler/characterizer/charutils.py index bc4beb88..a2140e51 100644 --- a/compiler/characterizer/charutils.py +++ b/compiler/characterizer/charutils.py @@ -5,7 +5,7 @@ from globals import OPTS def relative_compare(value1,value2,error_tolerance=0.001): """ This is used to compare relative values for convergence. """ - return (abs(value1 - value2) / max(value1,value2) <= error_tolerance) + return (abs(value1 - value2) / abs(max(value1,value2)) <= error_tolerance) def parse_spice_list(filename, key): diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index be37d8a9..9e0543d1 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -7,8 +7,9 @@ from .trim_spice import * from .charutils import * import utils from globals import OPTS +from .simulation import simulation -class delay(): +class delay(simulation): """Functions to measure the delay and power of an SRAM at a given address and data bit. @@ -26,24 +27,32 @@ class delay(): """ def __init__(self, sram, spfile, corner): - self.sram = sram - self.name = sram.name - self.word_size = self.sram.word_size - self.addr_size = self.sram.addr_size - self.num_cols = self.sram.num_cols - self.num_rows = self.sram.num_rows - self.num_banks = self.sram.num_banks - self.sp_file = spfile + simulation.__init__(self, sram, spfile, corner) # These are the member variables for a simulation + self.targ_read_ports = [] + self.targ_write_ports = [] self.period = 0 self.set_load_slew(0,0) self.set_corner(corner) - - def set_corner(self,corner): - """ Set the corner values """ - self.corner = corner - (self.process, self.vdd_voltage, self.temperature) = corner + self.create_signal_names() + + #Create global measure names. Should maybe be an input at some point. + self.create_measurement_names() + + def create_measurement_names(self): + """Create measurement names. The names themselves currently define the type of measurement""" + #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. + self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] + self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"] + + def create_signal_names(self): + self.addr_name = "A" + self.din_name = "DIN" + self.dout_name = "DOUT" + + #This is TODO once multiport control has been finalized. + #self.control_name = "CSB" def set_load_slew(self,load,slew): """ Set the load and slew """ @@ -78,15 +87,16 @@ class delay(): # instantiate the sram self.sf.write("\n* Instantiation of the SRAM\n") - self.stim.inst_sram(abits=self.addr_size, - dbits=self.word_size, - port_info=(self.total_port_num,self.readwrite_port_num,self.read_ports,self.write_ports), + self.stim.inst_sram(sram=self.sram, + port_signal_names=(self.addr_name,self.din_name,self.dout_name), + port_info=(len(self.all_ports),self.write_ports,self.read_ports), + abits=self.addr_size, + dbits=self.word_size, sram_name=self.name) - self.sf.write("\n* SRAM output loads\n") for port in self.read_ports: for i in range(self.word_size): - self.sf.write("CD{0}{1} DOUT{0}[{1}] 0 {2}f\n".format(port,i,self.load)) + self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port,i,self.dout_name,self.load)) def write_delay_stimulus(self): @@ -121,14 +131,15 @@ class delay(): self.sf.write("\n* Generation of control signals\n") self.gen_control() - self.sf.write("\n* Generation of global clock signal\n") - self.stim.gen_pulse(sig_name="CLK", - v1=0, - v2=self.vdd_voltage, - offset=self.period, - period=self.period, - t_rise=self.slew, - t_fall=self.slew) + self.sf.write("\n* Generation of Port clock signal\n") + for port in self.all_ports: + self.stim.gen_pulse(sig_name="CLK{0}".format(port), + v1=0, + v2=self.vdd_voltage, + offset=self.period, + period=self.period, + t_rise=self.slew, + t_fall=self.slew) self.write_delay_measures() @@ -144,9 +155,6 @@ class delay(): """ self.check_arguments() - # obtains list of time-points for each rising clk edge - #self.create_test_cycles() - # creates and opens stimulus file for writing temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) self.sf = open(temp_stim, "w") @@ -165,22 +173,23 @@ class delay(): self.sf.write("\n* Generation of data and address signals\n") for write_port in self.write_ports: for i in range(self.word_size): - self.stim.gen_constant(sig_name="DIN{0}[{1}] ".format(write_port, i), + self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i), v_val=0) - for port in range(self.total_port_num): + for port in self.all_ports: for i in range(self.addr_size): - self.stim.gen_constant(sig_name="A{0}[{1}]".format(port, i), + self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name,port, i), v_val=0) # generate control signals self.sf.write("\n* Generation of control signals\n") - for port in range(self.total_port_num): + for port in self.all_ports: self.stim.gen_constant(sig_name="CSB{0}".format(port), v_val=self.vdd_voltage) - if port in self.read_ports and port in self.write_ports: + if port in self.readwrite_ports: self.stim.gen_constant(sig_name="WEB{0}".format(port), v_val=self.vdd_voltage) self.sf.write("\n* Generation of global clock signal\n") - self.stim.gen_constant(sig_name="CLK", v_val=0) + for port in self.all_ports: + self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0) self.write_power_measures() @@ -189,87 +198,86 @@ class delay(): self.sf.close() + def get_delay_meas_values(self, delay_name, port): + """Get the values needed to generate a Spice measurement statement based on the name of the measurement.""" + debug.check('lh' in delay_name or 'hl' in delay_name, "Measure command {0} does not contain direction (lh/hl)") + trig_clk_name = "clk{0}".format(port) + meas_name="{0}{1}".format(delay_name, port) + targ_name = "{0}".format("{0}{1}_{2}".format(self.dout_name,port,self.probe_data)) + half_vdd = 0.5 * self.vdd_voltage + trig_slew_low = 0.1 * self.vdd_voltage + targ_slew_high = 0.9 * self.vdd_voltage + if 'delay' in delay_name: + trig_val = half_vdd + targ_val = half_vdd + trig_name = trig_clk_name + if 'lh' in delay_name: + trig_dir="RISE" + targ_dir="RISE" + trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read1"]] + else: + trig_dir="FALL" + targ_dir="FALL" + trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read0"]] + + elif 'slew' in delay_name: + trig_name = targ_name + if 'lh' in delay_name: + trig_val = trig_slew_low + targ_val = targ_slew_high + targ_dir = trig_dir = "RISE" + trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read1"]] + else: + trig_val = targ_slew_high + targ_val = trig_slew_low + targ_dir = trig_dir = "FALL" + trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read0"]] + else: + debug.error(1, "Measure command {0} not recognized".format(delay_name)) + return (meas_name,trig_name,targ_name,trig_val,targ_val,trig_dir,targ_dir,trig_td,targ_td) + def write_delay_measures_read_port(self, port): """ Write the measure statements to quantify the delay and power results for a read port. """ - - # Trigger on the clk of the appropriate cycle - trig_name = "clk" - #Target name should be an input to the function or a member variable. That way, the ports can be singled out for testing - targ_name = "{0}".format("DOUT{0}[{1}]".format(port,self.probe_data)) - trig_val = targ_val = 0.5 * self.vdd_voltage - - # Delay the target to measure after the negative edge - self.stim.gen_meas_delay(meas_name="DELAY_HL{0}".format(port), - trig_name=trig_name, - targ_name=targ_name, - trig_val=trig_val, - targ_val=targ_val, - trig_dir="RISE", - targ_dir="FALL", - trig_td=self.cycle_times[self.measure_cycles["read0_{0}".format(port)]], - targ_td=self.cycle_times[self.measure_cycles["read0_{0}".format(port)]]) - - self.stim.gen_meas_delay(meas_name="DELAY_LH{0}".format(port), - trig_name=trig_name, - targ_name=targ_name, - trig_val=trig_val, - targ_val=targ_val, - trig_dir="RISE", - targ_dir="RISE", - trig_td=self.cycle_times[self.measure_cycles["read1_{0}".format(port)]], - targ_td=self.cycle_times[self.measure_cycles["read1_{0}".format(port)]]) - - self.stim.gen_meas_delay(meas_name="SLEW_HL{0}".format(port), - trig_name=targ_name, - targ_name=targ_name, - trig_val=0.9*self.vdd_voltage, - targ_val=0.1*self.vdd_voltage, - trig_dir="FALL", - targ_dir="FALL", - trig_td=self.cycle_times[self.measure_cycles["read0_{0}".format(port)]], - targ_td=self.cycle_times[self.measure_cycles["read0_{0}".format(port)]]) - - self.stim.gen_meas_delay(meas_name="SLEW_LH{0}".format(port), - trig_name=targ_name, - targ_name=targ_name, - trig_val=0.1*self.vdd_voltage, - targ_val=0.9*self.vdd_voltage, - trig_dir="RISE", - targ_dir="RISE", - trig_td=self.cycle_times[self.measure_cycles["read1_{0}".format(port)]], - targ_td=self.cycle_times[self.measure_cycles["read1_{0}".format(port)]]) - + # add measure statements for delays/slews + for dname in self.delay_meas_names: + meas_values = self.get_delay_meas_values(dname, port) + self.stim.gen_meas_delay(*meas_values) + # add measure statements for power - t_initial = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]] - t_final = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]+1] - self.stim.gen_meas_power(meas_name="READ0_POWER{0}".format(port), - t_initial=t_initial, - t_final=t_final) - - t_initial = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]] - t_final = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]+1] - self.stim.gen_meas_power(meas_name="READ1_POWER{0}".format(port), - t_initial=t_initial, - t_final=t_final) - + for pname in self.power_meas_names: + if "read" not in pname: + continue + #Different naming schemes are used for the measure cycle dict and measurement names. + #TODO: make them the same so they can be indexed the same. + if '1' in pname: + t_initial = self.cycle_times[self.measure_cycles[port]["read1"]] + t_final = self.cycle_times[self.measure_cycles[port]["read1"]+1] + elif '0' in pname: + t_initial = self.cycle_times[self.measure_cycles[port]["read0"]] + t_final = self.cycle_times[self.measure_cycles[port]["read0"]+1] + self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port), + t_initial=t_initial, + t_final=t_final) + def write_delay_measures_write_port(self, port): """ Write the measure statements to quantify the power results for a write port. """ # add measure statements for power - t_initial = self.cycle_times[self.measure_cycles["write0_{0}".format(port)]] - t_final = self.cycle_times[self.measure_cycles["write0_{0}".format(port)]+1] - self.stim.gen_meas_power(meas_name="WRITE0_POWER{0}".format(port), - t_initial=t_initial, - t_final=t_final) - - t_initial = self.cycle_times[self.measure_cycles["write1_{0}".format(port)]] - t_final = self.cycle_times[self.measure_cycles["write1_{0}".format(port)]+1] - self.stim.gen_meas_power(meas_name="WRITE1_POWER{0}".format(port), - t_initial=t_initial, - t_final=t_final) + for pname in self.power_meas_names: + if "write" not in pname: + continue + t_initial = self.cycle_times[self.measure_cycles[port]["write0"]] + t_final = self.cycle_times[self.measure_cycles[port]["write0"]+1] + if '1' in pname: + t_initial = self.cycle_times[self.measure_cycles[port]["write1"]] + t_final = self.cycle_times[self.measure_cycles[port]["write1"]+1] + + self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port), + t_initial=t_initial, + t_final=t_final) def write_delay_measures(self): """ @@ -311,10 +319,8 @@ class delay(): starting point. """ debug.check(port in self.read_ports, "Characterizer requires a read port to determine a period.") - #Adding this as a sanity check for editing this function later. This function assumes period has been set previously - debug.check(self.period > 0, "Initial starting period not defined") + feasible_period = float(tech.spice["feasible_period"]) - #feasible_period = float(2.5)#What happens if feasible starting point is wrong? time_out = 9 while True: time_out -= 1 @@ -334,38 +340,33 @@ class delay(): if not success: feasible_period = 2 * feasible_period - break - feasible_delay_lh = results["delay_lh{0}".format(port)] - feasible_delay_hl = results["delay_hl{0}".format(port)] - feasible_slew_lh = results["slew_lh{0}".format(port)] - feasible_slew_hl = results["slew_hl{0}".format(port)] - - 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) + continue + + #Positions of measurements currently hardcoded. First 2 are delays, next 2 are slews + feasible_delays = [results[port][mname] for mname in self.delay_meas_names if "delay" in mname] + feasible_slews = [results[port][mname] for mname in self.delay_meas_names if "slew" in mname] + delay_str = "feasible_delay {0:.4f}ns/{1:.4f}ns".format(*feasible_delays) + slew_str = "slew {0:.4f}ns/{1:.4f}ns".format(*feasible_slews) debug.info(2, "feasible_period passed for Port {3}: {0}ns {1} {2} ".format(feasible_period, delay_str, slew_str, port)) - #Add feasible delays of port to dict - #feasible_delays_lh[port] = feasible_delay_lh - #feasible_delays_hl[port] = feasible_delay_hl if success: - debug.info(1, "Found feasible_period: {0}ns".format(feasible_period)) + debug.info(2, "Found feasible_period for port {0}: {1}ns".format(port, feasible_period)) self.period = feasible_period - return (feasible_delay_lh, feasible_delay_hl) + #Only return results related to input port. + return results[port] def find_feasible_period(self): """ Loops through all read ports determining the feasible period and collecting delay information from each port. """ - feasible_delays_lh = {} - feasible_delays_hl = {} - self.period = float(tech.spice["feasible_period"]) + feasible_delays = [{} for i in self.all_ports] - #Get initial feasible period from first port - (feasible_delays_lh[0], feasible_delays_hl[0]) = self.find_feasible_period_one_port(self.read_ports[0]) + #Get initial feasible delays from first port + feasible_delays[self.read_ports[0]] = self.find_feasible_period_one_port(self.read_ports[0]) previous_period = self.period @@ -374,23 +375,27 @@ class delay(): i = 1 while i < len(self.read_ports): port = self.read_ports[i] - (feasible_delays_lh[port], feasible_delays_hl[port]) = self.find_feasible_period_one_port(port) + #Only extract port values from the specified port, not the entire results. + feasible_delays[port].update(self.find_feasible_period_one_port(port)) #Function sets the period. Restart the entire process if period changes to collect accurate delays if self.period > previous_period: i = 0 else: i+=1 previous_period = self.period - return (feasible_delays_lh, feasible_delays_hl) + debug.info(1, "Found feasible_period: {0}ns".format(self.period)) + return feasible_delays - def parse_values(self, values_names, mult = 1.0): - """Parse multiple values in the timing output file. Optional multiplier.""" + def parse_values(self, values_names, port, mult = 1.0): + """Parse multiple values in the timing output file. Optional multiplier. + Return a dict of the input names and values. Port used for parsing file. + """ values = [] all_values_floats = True for vname in values_names: #ngspice converts all measure characters to lowercase, not tested on other sims - value = parse_spice_list("timing", vname.lower()) + value = parse_spice_list("timing", "{0}{1}".format(vname.lower(), port)) #Check if any of the values fail to parse if type(value)!=float: all_values_floats = False @@ -409,7 +414,10 @@ class delay(): works on the trimmed netlist by default, so powers do not include leakage of all cells. """ - result = {} + #Sanity Check + debug.check(self.period > 0, "Target simulation period non-positive") + + result = [{} for i in self.all_ports] # Checking from not data_value to data_value self.write_delay_stimulus() @@ -418,29 +426,29 @@ class delay(): #Loop through all targeted ports and collect delays and powers. #Too much duplicate code here. Try reducing for port in self.targ_read_ports: - delay_names = ["delay_hl{0}".format(port), "delay_lh{0}".format(port), - "slew_hl{0}".format(port), "slew_lh{0}".format(port)] - delays = self.parse_values(delay_names, 1e9) # scale delays to ns - if not self.check_valid_delays((delays[delay_names[0]],delays[delay_names[1]],delays[delay_names[2]],delays[delay_names[3]])): + debug.info(2, "Check delay values for port {}".format(port)) + delay_names = [mname for mname in self.delay_meas_names] + delays = self.parse_values(delay_names, port, 1e9) # scale delays to ns + if not self.check_valid_delays(delays): return (False,{}) - result.update(delays) + result[port].update(delays) - power_names = ["read0_power{0}".format(port), "read1_power{0}".format(port)] - powers = self.parse_values(power_names, 1e3) # scale power to mw + power_names = [mname for mname in self.power_meas_names if 'read' in mname] + powers = self.parse_values(power_names, port, 1e3) # scale power to mw #Check that power parsing worked. for name, power in powers.items(): if type(power)!=float: debug.error("Failed to Parse Power Values:\n\t\t{0}".format(powers),1) #Printing the entire dict looks bad. - result.update(powers) + result[port].update(powers) for port in self.targ_write_ports: - power_names = ["write0_power{0}".format(port), "write1_power{0}".format(port)] - powers = self.parse_values(power_names, 1e3) # scale power to mw + power_names = [mname for mname in self.power_meas_names if 'write' in mname] + powers = self.parse_values(power_names, port, 1e3) # scale power to mw #Check that power parsing worked. for name, power in powers.items(): if type(power)!=float: debug.error("Failed to Parse Power Values:\n\t\t{0}".format(powers),1) #Printing the entire dict looks bad. - result.update(powers) + result[port].update(powers) # The delay is from the negative edge for our SRAM return (True,result) @@ -470,10 +478,13 @@ class delay(): #key=raw_input("press return to continue") return (leakage_power*1e3, trim_leakage_power*1e3) - def check_valid_delays(self, delay_tuple): + def check_valid_delays(self, delay_dict): """ Check if the measurements are defined and if they are valid. """ - - (delay_hl, delay_lh, slew_hl, slew_lh) = delay_tuple + #Hard coded names currently + delay_hl = delay_dict["delay_hl"] + delay_lh = delay_dict["delay_lh"] + slew_hl = delay_dict["slew_hl"] + slew_lh = delay_dict["slew_lh"] 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 @@ -487,7 +498,8 @@ class delay(): 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: + half_period = self.period/2 #high-to-low delays start at neg. clk edge, so they need to be less than half_period + if abs(delay_hl)>half_period or abs(delay_lh)>self.period or abs(slew_hl)>half_period or abs(slew_lh)>self.period: 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)) @@ -499,11 +511,11 @@ class delay(): return True - def find_min_period(self, feasible_delays_lh, feasible_delays_hl): + def find_min_period(self, feasible_delays): """ - Determine the minimum period for all ports. + Determine a single minimum period for all ports. """ - + feasible_period = ub_period = self.period lb_period = 0.0 target_period = 0.5 * (ub_period + lb_period) @@ -511,7 +523,7 @@ class delay(): #Find the minimum period for all ports. Start at one port and perform binary search then use that delay as a starting position. #For testing purposes, only checks read ports. for port in self.read_ports: - target_period = self.find_min_period_one_port(feasible_delays_lh, feasible_delays_hl, port, lb_period, ub_period, target_period) + target_period = self.find_min_period_one_port(feasible_delays, port, lb_period, ub_period, target_period) #The min period of one port becomes the new lower bound. Reset the upper_bound. lb_period = target_period ub_period = feasible_period @@ -521,10 +533,10 @@ class delay(): self.targ_write_ports = [] return target_period - def find_min_period_one_port(self, feasible_delays_lh, feasible_delays_hl, port, lb_period, ub_period, target_period): + def find_min_period_one_port(self, feasible_delays, port, lb_period, ub_period, target_period): """ Searches for the smallest period with output delays being within 5% of - long period. For the current logic to characterize multiport, bound are required as an input. + long period. For the current logic to characterize multiport, bounds are required as an input. """ #previous_period = ub_period = self.period @@ -546,24 +558,21 @@ class delay(): lb_period, port)) - if self.try_period(feasible_delays_lh, feasible_delays_hl): + if self.try_period(feasible_delays): ub_period = target_period else: lb_period = target_period if relative_compare(ub_period, lb_period, error_tolerance=0.05): - # ub_period is always feasible. When done with a port, set the target period of the next port as the lower bound - # and reset the upperbound + # ub_period is always feasible. return ub_period - #target_period = lb_period = ub_period - #ub_period = previous_period - #break - + #Update target target_period = 0.5 * (ub_period + lb_period) + #key=input("press return to continue") - def try_period(self, feasible_delays_lh, feasible_delays_hl): + def try_period(self, feasible_delays): """ This tries to simulate a period and checks if the result works. If it does and the delay is within 5% still, it returns True. @@ -574,27 +583,26 @@ class delay(): return False #Check the values of target readwrite and read ports. Write ports do not produce delays in this current version - for port in self.targ_read_ports: - delay_hl = results["delay_hl{0}".format(port)] - delay_lh = results["delay_lh{0}".format(port)] - slew_hl = results["slew_hl{0}".format(port)] - slew_lh = results["slew_lh{0}".format(port)] + for port in self.targ_read_ports: + for dname in self.delay_meas_names: #check that the delays and slews do not degrade with tested period. + + #FIXME: This is a hack solution to fix the min period search. The slew will always be based on the period when there + #is a column mux. Therefore, the checks are skipped for this condition. This is hard to solve without changing the netlist. + #Delays/slews based on the period will cause the min_period search to come to the wrong period. + if self.sram.col_addr_size>0 and "slew" in dname: + continue - if not relative_compare(delay_lh,feasible_delays_lh[port],error_tolerance=0.05): - debug.info(2,"Delay too big {0} vs {1}".format(delay_lh,feasible_delays_lh[port])) - return False - elif not relative_compare(delay_hl,feasible_delays_hl[port],error_tolerance=0.05): - debug.info(2,"Delay too big {0} vs {1}".format(delay_hl,feasible_delays_hl[port])) - return False + if not relative_compare(results[port][dname],feasible_delays[port][dname],error_tolerance=0.05): + debug.info(2,"Delay too big {0} vs {1}".format(results[port][dname],feasible_delays[port][dname])) + return False #key=raw_input("press return to continue") - - debug.info(2,"Successful period {0}, Port {5}, delay_hl={1}ns, delay_lh={2}ns slew_hl={3}ns slew_lh={4}ns".format(self.period, - delay_hl, - delay_lh, - slew_hl, - slew_lh, - port)) + + #Dynamic way to build string. A bit messy though. + delay_str = ', '.join("{0}={1}ns".format(mname, results[port][mname]) for mname in self.delay_meas_names) + debug.info(2,"Successful period {0}, Port {2}, {1}".format(self.period, + delay_str, + port)) return True def set_probe(self,probe_address, probe_data): @@ -633,12 +641,11 @@ class delay(): """ Main function to characterize an SRAM for a table. Computes both delay and power characterization. """ + #Dict to hold all characterization values + char_sram_data = {} + self.set_probe(probe_address, probe_data) - self.create_port_names() - - self.create_char_data_dict() - self.load=max(loads) self.slew=max(slews) # This is for debugging a full simulation @@ -650,48 +657,41 @@ class delay(): # slew=0.04 # self.try_period(target_period, feasible_delay_lh, feasible_delay_hl) # sys.exit(1) - - #For debugging, skips characterization and returns dummy values. - # char_data = self.char_data - # i = 1.0 - # for slew in slews: - # for load in loads: - # for k,v in char_data.items(): - # char_data[k].append(i) - # i+=1.0 - # char_data["min_period"] = i - # char_data["leakage_power"] = i+1.0 - # return char_data # 1) Find a feasible period and it's corresponding delays using the trimmed array. - (feasible_delays_lh, feasible_delays_hl) = self.find_feasible_period() - #Check all the delays - for k,v in feasible_delays_lh.items(): - debug.check(v>0,"Negative delay may not be possible") - for k,v in feasible_delays_hl.items(): - debug.check(v>0,"Negative delay may not be possible") - + feasible_delays = self.find_feasible_period() # 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_delays_lh, feasible_delays_hl) + min_period = self.find_min_period(feasible_delays) debug.check(type(min_period)==float,"Couldn't find minimum period.") debug.info(1, "Min Period Found: {0}ns".format(min_period)) - self.char_data["min_period"] = round_time(min_period) + char_sram_data["min_period"] = round_time(min_period) # 3) Find the leakage power of the trimmmed and UNtrimmed arrays. (full_array_leakage, trim_array_leakage)=self.run_power_simulation() - self.char_data["leakage_power"]=full_array_leakage + char_sram_data["leakage_power"]=full_array_leakage leakage_offset = full_array_leakage - trim_array_leakage - # 4) At the minimum period, measure the delay, slew and power for all slew/load pairs. - self.simulate_loads_and_slews(slews, loads, leakage_offset) - - return self.char_data + self.period = min_period + char_port_data = self.simulate_loads_and_slews(slews, loads, leakage_offset) + + #FIXME: low-to-high delays are altered to be independent of the period. This makes the lib results less accurate. + self.alter_lh_char_data(char_port_data) + + return (char_sram_data, char_port_data) + def alter_lh_char_data(self, char_port_data): + """Copies high-to-low data to low-to-high data to make them consistent on the same clock edge.""" + #This is basically a hack solution which should be removed/fixed later. + for port in self.all_ports: + char_port_data[port]['delay_lh'] = char_port_data[port]['delay_hl'] + char_port_data[port]['slew_lh'] = char_port_data[port]['slew_hl'] + def simulate_loads_and_slews(self, slews, loads, leakage_offset): - """Simulate all specified output loads and input slews pairs""" - #Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways. + """Simulate all specified output loads and input slews pairs of all ports""" + measure_data = self.get_empty_measure_data_dict() + #Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways. self.targ_read_ports = self.read_ports self.targ_write_ports = self.write_ports for slew in slews: @@ -700,136 +700,38 @@ class delay(): # 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(): - if "power" in k: - # Subtract partial array leakage and add full array leakage for the power measures - self.char_data[k].append(v + leakage_offset) - else: - self.char_data[k].append(v) - - def add_data(self, data, port): - """ Add the array of data values """ - debug.check(len(data)==self.word_size, "Invalid data word size.") - debug.check(port < len(self.data_values), "Port number cannot index data values.") - index = 0 - for c in data: - if c=="0": - self.data_values[port][index].append(0) - elif c=="1": - self.data_values[port][index].append(1) - else: - debug.error("Non-binary data string",1) - index += 1 - - def add_address(self, address, port): - """ 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[port][index].append(0) - elif c=="1": - self.addr_values[port][index].append(1) - else: - debug.error("Non-binary address string",1) - index += 1 + debug.info(1, "Simulation Passed: Port {0} slew={1} load={2}".format("All", self.slew,self.load)) + #The results has a dict for every port but dicts can be empty (e.g. ports were not targeted). + for port in self.all_ports: + for mname,value in delay_results[port].items(): + if "power" in mname: + # Subtract partial array leakage and add full array leakage for the power measures + measure_data[port][mname].append(value + leakage_offset) + else: + measure_data[port][mname].append(value) + return measure_data - def add_noop_one_port(self, address, data, port): - """ Add the control values for a noop to a single port. """ - #This is to be used as a helper function for the other add functions. Cycle and comments are omitted. - self.add_control_one_port(port, "noop") - if port in self.write_ports: - self.add_data(data,port) - self.add_address(address, port) - - def add_noop_all_ports(self, comment, address, data): - """ Add the control values for a noop to all ports. """ - self.cycle_comments.append("Cycle {0:2d}\tPort All\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 - - for port in range(self.total_port_num): - self.add_noop_one_port(address, data, port) - - - def add_read(self, comment, address, data, port): - """ Add the control values for a read cycle. """ - debug.check(port in self.read_ports, "Cannot add read cycle to a write port.") - self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments), - self.t_current, - comment, - port)) - self.cycle_times.append(self.t_current) - self.t_current += self.period - self.add_control_one_port(port, "read") - - #If the port is also a readwrite then add data. - if port in self.write_ports: - self.add_data(data,port) - self.add_address(address, port) - - #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port - noop_data = "0"*self.word_size - #Add noops to all other ports. - for unselected_port in range(self.total_port_num): - if unselected_port != port: - self.add_noop_one_port(address, noop_data, unselected_port) - - def add_write(self, comment, address, data, port): - """ Add the control values for a write cycle. """ - debug.check(port in self.write_ports, "Cannot add read cycle to a read port.") - self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments), - self.t_current, - comment, - port)) - self.cycle_times.append(self.t_current) - self.t_current += self.period - - self.add_control_one_port(port, "write") - self.add_data(data,port) - self.add_address(address,port) - - #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port - noop_data = "0"*self.word_size - #Add noops to all other ports. - for unselected_port in range(self.total_port_num): - if unselected_port != port: - self.add_noop_one_port(address, noop_data, unselected_port) - - def add_control_one_port(self, port, op): - """Appends control signals for operation to a given port""" - #Determine values to write to port - web_val = 1 - csb_val = 1 - if op == "read": - csb_val = 0 - elif op == "write": - csb_val = 0 - web_val = 0 - elif op != "noop": - debug.error("Could not add control signals for port {0}. Command {1} not recognized".format(port,op),1) - - #Append the values depending on the type of port - self.csb_values[port].append(csb_val) - #If port is in both lists, add rw control signal. Condition indicates its a RW port. - if port < len(self.web_values): - self.web_values[port].append(web_val) - - def gen_test_cycles_one_port(self, read_port, write_port): - """Intended but not implemented: Returns a list of key time-points [ns] of the waveform (each rising edge) - of the cycles to do a timing evaluation of a single port. Current: Values overwritten for multiple calls""" - - # Create the inverse address for a scratch address + def calculate_inverse_address(self): + """Determine dummy test address based on probe address and column mux size.""" + #The inverse address needs to share the same bitlines as the probe address as the trimming will remove all other bitlines + #This is only an issue when there is a column mux and the address maps to different bitlines. + column_addr = self.probe_address[:self.sram.col_addr_size] #do not invert this part inverse_address = "" - for c in self.probe_address: + for c in self.probe_address[self.sram.col_addr_size:]: #invert everything else if c=="0": inverse_address += "1" elif c=="1": inverse_address += "0" else: debug.error("Non-binary address string",1) + return inverse_address+column_addr + + def gen_test_cycles_one_port(self, read_port, write_port): + """Sets a list of key time-points [ns] of the waveform (each rising edge) + of the cycles to do a timing evaluation of a single port """ + + # Create the inverse address for a scratch address + inverse_address = self.calculate_inverse_address() # For now, ignore data patterns and write ones or zeros data_ones = "1"*self.word_size @@ -839,44 +741,38 @@ class delay(): self.add_noop_all_ports("Idle cycle (no positive clock edge)", inverse_address, data_zeros) - self.add_write("W data 1 address 0..00", + self.add_write("W data 1 address {}".format(inverse_address), inverse_address,data_ones,write_port) - self.add_write("W data 0 address 11..11 to write value", + self.add_write("W data 0 address {} to write value".format(self.probe_address), self.probe_address,data_zeros,write_port) - self.measure_cycles["write0_{0}".format(write_port)] = len(self.cycle_times)-1 - #self.write0_cycle=len(self.cycle_times)-1 # Remember for power measure + self.measure_cycles[write_port]["write0"] = len(self.cycle_times)-1 # 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", + self.add_read("R data 1 address {} to set DOUT caps".format(inverse_address), inverse_address,data_zeros,read_port) - self.add_read("R data 0 address 11..11 to check W0 worked", + self.add_read("R data 0 address {} to check W0 worked".format(self.probe_address), self.probe_address,data_zeros,read_port) - self.measure_cycles["read0_{0}".format(read_port)] = len(self.cycle_times)-1 - #self.read0_cycle=len(self.cycle_times)-1 # Remember for power measure + self.measure_cycles[read_port]["read0"] = len(self.cycle_times)-1 self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)", inverse_address,data_zeros) - #Does not seem like is is used anywhere commenting out for now. - #self.idle_cycle=len(self.cycle_times)-1 # Remember for power measure - self.add_write("W data 1 address 11..11 to write value", + self.add_write("W data 1 address {} to write value".format(self.probe_address), self.probe_address,data_ones,write_port) - self.measure_cycles["write1_{0}".format(write_port)] = len(self.cycle_times)-1 - #self.write1_cycle=len(self.cycle_times)-1 # Remember for power measure + self.measure_cycles[write_port]["write1"] = len(self.cycle_times)-1 - self.add_write("W data 0 address 00..00 to clear DIN caps", + self.add_write("W data 0 address {} to clear DIN caps".format(inverse_address), inverse_address,data_zeros,write_port) # 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", + self.add_read("R data 0 address {} to clear DOUT caps".format(inverse_address), inverse_address,data_zeros,read_port) - self.add_read("R data 1 address 11..11 to check W1 worked", + self.add_read("R data 1 address {} to check W1 worked".format(self.probe_address), self.probe_address,data_zeros,read_port) - self.measure_cycles["read1_{0}".format(read_port)] = len(self.cycle_times)-1 - #self.read1_cycle=len(self.cycle_times)-1 # Remember for power measure + self.measure_cycles[read_port]["read1"] = len(self.cycle_times)-1 self.add_noop_all_ports("Idle cycle (if read takes >1 cycle))", self.probe_address,data_zeros) @@ -888,6 +784,10 @@ class delay(): elif not get_read_port and len(self.write_ports) > 0: return self.write_ports[0] return None + + def set_stimulus_variables(self): + simulation.set_stimulus_variables(self) + self.measure_cycles = [{} for port in self.all_ports] def create_test_cycles(self): """Returns a list of key time-points [ns] of the waveform (each rising edge) @@ -895,35 +795,16 @@ class delay(): and does not need a rising edge.""" #Using this requires setting at least one port to target for simulation. if len(self.targ_write_ports) == 0 and len(self.targ_read_ports) == 0: - debug.error("No ports selected for characterization.",1) - - # Start at time 0 - self.t_current = 0 - - # Cycle times (positive edge) with comment - self.cycle_comments = [] - self.cycle_times = [] - self.measure_cycles = {} - - # Control signals for ports. These are not the final signals and will likely be changed later. - #write enable bar for readwrite ports to control read or write - self.web_values = [[] for i in range(self.readwrite_port_num)] - #csb represents a basic "enable" signal that all ports have. - self.csb_values = [[] for i in range(self.total_port_num)] - - # Address and data values for each address/data bit. A dict of 3d lists of size #ports x bits x cycles. - self.data_values=[[[] for i in range(self.addr_size)]]*len(self.write_ports) - self.addr_values=[[[] for i in range(self.addr_size)]]*self.total_port_num - + debug.error("No port selected for characterization.",1) + self.set_stimulus_variables() + #Get any available read/write port in case only a single write or read ports is being characterized. cur_read_port = self.get_available_port(get_read_port=True) cur_write_port = self.get_available_port(get_read_port=False) - - #These checks should be superceded by check_arguments which should have been called earlier, so this is a double check. debug.check(cur_read_port != None, "Characterizer requires at least 1 read port") debug.check(cur_write_port != None, "Characterizer requires at least 1 write port") - #Characterizing the remaining target ports. Not the final design. + #Create test cycles for specified target ports. write_pos = 0 read_pos = 0 while True: @@ -942,51 +823,51 @@ class delay(): #Add test cycle of read/write port pair. One port could have been used already, but the other has not. self.gen_test_cycles_one_port(cur_read_port, cur_write_port) - def analytical_delay(self,sram, slews, loads): + def analytical_delay(self, slews, loads): """ Return the analytical model results for the SRAM. """ - debug.check(OPTS.num_rw_ports < 2 and OPTS.num_w_ports < 1 and OPTS.num_r_ports < 1 , - "Analytical characterization does not currently support multiport.") + if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0: + debug.warning("Analytical characterization results are not supported for multiport.") - delay_lh = [] - delay_hl = [] - slew_lh = [] - slew_hl = [] + power = self.analytical_power(slews, loads) + port_data = self.get_empty_measure_data_dict() for slew in slews: for load in loads: self.set_load_slew(load,slew) - bank_delay = sram.analytical_delay(self.slew,self.load) - # Convert from ps to ns - delay_lh.append(bank_delay.delay/1e3) - delay_hl.append(bank_delay.delay/1e3) - slew_lh.append(bank_delay.slew/1e3) - slew_hl.append(bank_delay.slew/1e3) + bank_delay = self.sram.analytical_delay(self.vdd_voltage, self.slew,self.load) + for port in self.all_ports: + for mname in self.delay_meas_names+self.power_meas_names: + if "power" in mname: + port_data[port][mname].append(power.dynamic) + elif "delay" in mname: + port_data[port][mname].append(bank_delay[port].delay/1e3) + elif "slew" in mname: + port_data[port][mname].append(bank_delay[port].slew/1e3) + else: + debug.error("Measurement name not recognized: {}".format(mname),1) + sram_data = { "min_period": 0, + "leakage_power": power.leakage} + + return (sram_data,port_data) - power = sram.analytical_power(self.process, self.vdd_voltage, self.temperature, load) + + def analytical_power(self, slews, loads): + """Get the dynamic and leakage power from the SRAM""" + #slews unused, only last load is used + load = loads[-1] + power = self.sram.analytical_power(self.process, self.vdd_voltage, self.temperature, load) #convert from nW to mW power.dynamic /= 1e6 power.leakage /= 1e6 debug.info(1,"Dynamic Power: {0} mW".format(power.dynamic)) debug.info(1,"Leakage Power: {0} mW".format(power.leakage)) + return power - data = {"min_period": 0, - "delay_lh0": delay_lh, - "delay_hl0": delay_hl, - "slew_lh0": slew_lh, - "slew_hl0": slew_hl, - "read0_power0": power.dynamic, - "read1_power0": power.dynamic, - "write0_power0": power.dynamic, - "write1_power0": power.dynamic, - "leakage_power": power.leakage - } - return data - def gen_data(self): """ Generates the PWL data inputs for a simulation timing test. """ for write_port in self.write_ports: for i in range(self.word_size): - sig_name="DIN{0}[{1}] ".format(write_port, i) + sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05) def gen_addr(self): @@ -994,48 +875,22 @@ class delay(): Generates the address inputs for a simulation timing test. This alternates between all 1's and all 0's for the address. """ - for port in range(self.total_port_num): + for port in self.all_ports: for i in range(self.addr_size): - sig_name = "A{0}[{1}]".format(port,i) + sig_name = "{0}{1}_{2}".format(self.addr_name,port,i) self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][i], self.period, self.slew, 0.05) def gen_control(self): """ Generates the control signals """ - for port in range(self.total_port_num): + for port in self.all_ports: self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) + if port in self.readwrite_ports: + self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) - for readwrite_port in range(self.readwrite_port_num): - self.stim.gen_pwl("WEB{0}".format(readwrite_port), self.cycle_times, self.web_values[readwrite_port], self.period, self.slew, 0.05) - - def create_port_names(self): - """Generates the port names to be used in characterization and sets default simulation target ports""" - self.write_ports = [] - self.read_ports = [] - self.total_port_num = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports - - #save a member variable to avoid accessing global. readwrite ports have different control signals. - self.readwrite_port_num = OPTS.num_rw_ports - - #Generate the port names. readwrite ports are required to be added first for this to work. - for readwrite_port_num in range(OPTS.num_rw_ports): - self.read_ports.append(readwrite_port_num) - self.write_ports.append(readwrite_port_num) - #This placement is intentional. It makes indexing input data easier. See self.data_values - for write_port_num in range(OPTS.num_rw_ports, OPTS.num_rw_ports+OPTS.num_w_ports): - self.write_ports.append(write_port_num) - for read_port_num in range(OPTS.num_rw_ports+OPTS.num_w_ports, OPTS.num_rw_ports+OPTS.num_w_ports+OPTS.num_r_ports): - self.read_ports.append(read_port_num) - - #Set the default target ports for simulation. Default is all the ports. - self.targ_read_ports = self.read_ports - self.targ_write_ports = self.write_ports - - def create_char_data_dict(self): - """Make a dict of lists for each type of measurement to append results to""" - #Making this a member variable may not be the best option, but helps reduce code clutter - self.char_data = {} - for port in range(self.total_port_num): - for m in ["delay_lh", "delay_hl", "slew_lh", "slew_hl", "read0_power", - "read1_power", "write0_power", "write1_power"]: - self.char_data ["{0}{1}".format(m,port)]=[] \ No newline at end of file + 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 diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py new file mode 100644 index 00000000..ca05648a --- /dev/null +++ b/compiler/characterizer/functional.py @@ -0,0 +1,286 @@ +import sys,re,shutil +from design import design +import debug +import math +import tech +import random +from .stimuli import * +from .charutils import * +import utils +from globals import OPTS + +from .simulation import simulation +from .delay import delay + +class functional(simulation): + """ + Functions to write random data values to a random address then read them back and check + for successful SRAM operation. + """ + + def __init__(self, sram, spfile, corner): + simulation.__init__(self, sram, spfile, corner) + + # Seed the characterizer with a constant seed for unit tests + if OPTS.is_unit_test: + random.seed(12345) + + self.set_corner(corner) + self.set_spice_constants() + #self.set_feasible_period(sram, spfile, corner) + self.set_stimulus_variables() + self.create_signal_names() + + + # Number of checks can be changed + self.num_cycles = 2 + self.stored_words = {} + self.write_check = [] + self.read_check = [] + + def run(self, feasible_period=None): + if feasible_period: #period defaults to tech.py feasible period otherwise. + self.period = feasible_period + # Generate a random sequence of reads and writes + self.write_random_memory_sequence() + + # Run SPICE simulation + self.write_functional_stimulus() + self.stim.run_sim() + + # read DOUT values from SPICE simulation. If the values do not fall within the noise margins, return the error. + (success, error) = self.read_stim_results() + if not success: + return (0, error) + + # Check read values with written values. If the values do not match, return an error. + return self.check_stim_results() + + def write_random_memory_sequence(self): + rw_ops = ["noop", "write", "read"] + w_ops = ["noop", "write"] + r_ops = ["noop", "read"] + rw_read_din_data = "0"*self.word_size + check = 0 + + # First cycle idle + comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, 0, self.t_current) + self.add_noop_all_ports(comment, "0"*self.addr_size, "0"*self.word_size) + + # Write at least once + addr = self.gen_addr() + word = self.gen_data() + comment = self.gen_cycle_comment("write", word, addr, 0, self.t_current) + self.add_write(comment, addr, word, 0) + self.stored_words[addr] = word + + # Read at least once. For multiport, it is important that one read cycle uses all RW and R port to read from the same address simultaniously. + # This will test the viablilty of the transistor sizing in the bitcell. + for port in self.all_ports: + if port in self.write_ports: + self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port) + else: + comment = self.gen_cycle_comment("read", word, addr, port, self.t_current) + self.add_read_one_port(comment, addr, rw_read_din_data, port) + self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check]) + check += 1 + self.cycle_times.append(self.t_current) + self.t_current += self.period + + # Perform a random sequence of writes and reads on random ports, using random addresses and random words + for i in range(self.num_cycles): + w_addrs = [] + for port in self.all_ports: + if port in self.readwrite_ports: + op = random.choice(rw_ops) + elif port in self.write_ports: + op = random.choice(w_ops) + else: + op = random.choice(r_ops) + + if op == "noop": + addr = "0"*self.addr_size + word = "0"*self.word_size + self.add_noop_one_port(addr, word, port) + elif op == "write": + addr = self.gen_addr() + word = self.gen_data() + # two ports cannot write to the same address + if addr in w_addrs: + self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port) + else: + comment = self.gen_cycle_comment("write", word, addr, port, self.t_current) + self.add_write_one_port(comment, addr, word, port) + self.stored_words[addr] = word + w_addrs.append(addr) + else: + (addr,word) = random.choice(list(self.stored_words.items())) + # cannot read from an address that is currently being written to + if addr in w_addrs: + self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port) + else: + comment = self.gen_cycle_comment("read", word, addr, port, self.t_current) + self.add_read_one_port(comment, addr, rw_read_din_data, port) + self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check]) + check += 1 + + self.cycle_times.append(self.t_current) + self.t_current += self.period + + # Last cycle idle needed to correctly measure the value on the second to last clock edge + comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, 0, self.t_current) + self.add_noop_all_ports(comment, "0"*self.addr_size, "0"*self.word_size) + + def read_stim_results(self): + # Extrat DOUT values from spice timing.lis + for (word, dout_port, eo_period, check) in self.write_check: + sp_read_value = "" + for bit in range(self.word_size): + value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check)) + if value > self.v_high: + sp_read_value = "1" + sp_read_value + elif value < self.v_low: + sp_read_value = "0" + sp_read_value + else: + error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port, + bit, + value, + eo_period, + self.v_low, + self.v_high) + return (0, error) + + self.read_check.append([sp_read_value, dout_port, eo_period, check]) + return (1, "SUCCESS") + + def check_stim_results(self): + for i in range(len(self.write_check)): + if self.write_check[i][0] != self.read_check[i][0]: + error = "FAILED: {0} value {1} does not match written value {2} read during cycle {3} at time {4}n".format(self.read_check[i][1], + self.read_check[i][0], + self.write_check[i][0], + int((self.read_check[i][2]-self.period)/self.period), + self.read_check[i][2]) + return(0, error) + return(1, "SUCCESS") + + def gen_data(self): + """ Generates a random word to write. """ + rand = random.randint(0,(2**self.word_size)-1) + data_bits = self.convert_to_bin(rand,False) + return data_bits + + def gen_addr(self): + """ Generates a random address value to write to. """ + rand = random.randint(0,(2**self.addr_size)-1) + addr_bits = self.convert_to_bin(rand,True) + return addr_bits + + def get_data(self): + """ Gets an available address and corresponding word. """ + # Currently unused but may need later depending on how the functional test develops + addr = random.choice(self.stored_words.keys()) + word = self.stored_words[addr] + return (addr,word) + + def convert_to_bin(self,value,is_addr): + """ Converts addr & word to usable binary values. """ + new_value = str.replace(bin(value),"0b","") + if(is_addr): + expected_value = self.addr_size + else: + expected_value = self.word_size + for i in range (expected_value - len(new_value)): + new_value = "0" + new_value + + #print("Binary Conversion: {} to {}".format(value, new_value)) + return new_value + + def create_signal_names(self): + self.addr_name = "A" + self.din_name = "DIN" + self.dout_name = "DOUT" + + def write_functional_stimulus(self): + """ Writes SPICE stimulus. """ + temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) + self.sf = open(temp_stim,"w") + self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period)) + self.stim = stimuli(self.sf,self.corner) + + #Write include statements + self.sram_sp_file = "{}sram.sp".format(OPTS.openram_temp) + shutil.copy(self.sp_file, self.sram_sp_file) + self.stim.write_include(self.sram_sp_file) + + #Write Vdd/Gnd statements + self.sf.write("\n* Global Power Supplies\n") + self.stim.write_supply() + + #Instantiate the SRAM + self.sf.write("\n* Instantiation of the SRAM\n") + self.stim.inst_sram(sram=self.sram, + port_signal_names=(self.addr_name,self.din_name,self.dout_name), + port_info=(len(self.all_ports), self.write_ports, self.read_ports), + abits=self.addr_size, + dbits=self.word_size, + sram_name=self.name) + + # Add load capacitance to each of the read ports + self.sf.write("\n* SRAM output loads\n") + for port in self.read_ports: + for bit in range(self.word_size): + sig_name="{0}{1}_{2} ".format(self.dout_name, port, bit) + self.sf.write("CD{0}{1} {2} 0 {3}f\n".format(port, bit, sig_name, self.load)) + + # Write debug comments to stim file + self.sf.write("\n\n * Sequence of operations\n") + for comment in self.fn_cycle_comments: + self.sf.write("*{}\n".format(comment)) + + # Generate data input bits + self.sf.write("\n* Generation of data and address signals\n") + for port in self.write_ports: + for bit in range(self.word_size): + sig_name="{0}{1}_{2} ".format(self.din_name, port, bit) + self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05) + + # Generate address bits + for port in self.all_ports: + for bit in range(self.addr_size): + sig_name="{0}{1}_{2} ".format(self.addr_name, port, bit) + self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][bit], self.period, self.slew, 0.05) + + # Generate control signals + self.sf.write("\n * Generation of control signals\n") + for port in self.all_ports: + self.stim.gen_pwl("CSB{}".format(port), self.cycle_times , self.csb_values[port], self.period, self.slew, 0.05) + + for port in self.readwrite_ports: + self.stim.gen_pwl("WEB{}".format(port), self.cycle_times , self.web_values[port], self.period, self.slew, 0.05) + + # Generate CLK signals + for port in self.all_ports: + self.stim.gen_pulse(sig_name="{0}{1}".format(tech.spice["clk"], port), + v1=self.gnd_voltage, + v2=self.vdd_voltage, + offset=self.period, + period=self.period, + t_rise=self.slew, + t_fall=self.slew) + + # Generate DOUT value measurements + self.sf.write("\n * Generation of dout measurements\n") + for (word, dout_port, eo_period, check) in self.write_check: + t_intital = eo_period - 0.01*self.period + t_final = eo_period + 0.01*self.period + for bit in range(self.word_size): + self.stim.gen_meas_value(meas_name="V{0}_{1}ck{2}".format(dout_port,bit,check), + dout="{0}_{1}".format(dout_port,bit), + t_intital=t_intital, + t_final=t_final) + + self.stim.write_control(self.cycle_times[-1] + self.period) + self.sf.close() + + diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 9f04fd7f..7b10eb8f 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -1,6 +1,7 @@ import os,sys,re import debug import math +import datetime from .setup_hold import * from .delay import * from .charutils import * @@ -12,16 +13,12 @@ class lib: """ lib file generation.""" def __init__(self, out_dir, sram, sp_file, use_model=OPTS.analytical_delay): - #Temporary Workaround to here to set num of ports. Crashes if set in config file. - #OPTS.num_rw_ports = 2 - #OPTS.num_r_ports = 1 - #OPTS.num_w_ports = 1 self.out_dir = out_dir self.sram = sram self.sp_file = sp_file self.use_model = use_model - self.gen_port_names() #copy and paste from delay.py, names are not final will likely be changed later. + self.set_port_indices() self.prepare_tables() @@ -29,26 +26,14 @@ class lib: self.characterize_corners() - def gen_port_names(self): - """Generates the port names to be written to the lib file""" - #This is basically a copy and paste of whats in delay.py as well. Something more efficient should be done here. - self.write_ports = [] - self.read_ports = [] - self.total_port_num = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports - - #save a member variable to avoid accessing global. readwrite ports have different control signals. - self.readwrite_port_num = OPTS.num_rw_ports - - #Generate the port names. readwrite ports are required to be added first for this to work. - for readwrite_port_num in range(OPTS.num_rw_ports): - self.read_ports.append(readwrite_port_num) - self.write_ports.append(readwrite_port_num) - #This placement is intentional. It makes indexing input data easier. See self.data_values - for read_port_num in range(OPTS.num_rw_ports, OPTS.num_r_ports): - self.read_ports.append(read_port_num) - for write_port_num in range(OPTS.num_rw_ports+OPTS.num_r_ports, OPTS.num_w_ports): - self.write_ports.append(write_port_num) - + def set_port_indices(self): + """Copies port information set in the SRAM instance""" + self.total_port_num = len(self.sram.all_ports) + self.all_ports = self.sram.all_ports + self.readwrite_ports = self.sram.readwrite_ports + self.read_ports = self.sram.read_ports + self.write_ports = self.sram.write_ports + def prepare_tables(self): """ Determine the load/slews if they aren't specified in the config file. """ # These are the parameters to determine the table sizes @@ -100,7 +85,7 @@ class lib: debug.info(1,"Writing to {0}".format(lib_name)) self.characterize() self.lib.close() - + self.parse_info(self.corner,lib_name) def characterize(self): """ Characterize the current corner. """ @@ -110,21 +95,21 @@ class lib: self.write_header() - #Loop over all readwrite ports. This is debugging. Will change later. - for port in range(self.total_port_num): + #Loop over all ports. + for port in self.all_ports: #set the read and write port as inputs. self.write_data_bus(port) self.write_addr_bus(port) self.write_control_pins(port) #need to split this into sram and port control signals - - self.write_clk_timing_power() + self.write_clk_timing_power(port) self.write_footer() def write_footer(self): """ Write the footer """ - self.lib.write("}\n") + self.lib.write(" }\n") #Closing brace for the cell + self.lib.write("}\n") #Closing brace for the library def write_header(self): """ Write the header information """ @@ -153,7 +138,7 @@ class lib: self.lib.write(" dont_touch : true;\n") self.lib.write(" area : {};\n\n".format(self.sram.width * self.sram.height)) - #Build string of all control signals. This is subject to change once control signals finalized. + #Build string of all control signals. control_str = 'CSb0' #assume at least 1 port for i in range(1, self.total_port_num): control_str += ' & CSb{0}'.format(i) @@ -161,7 +146,7 @@ class lib: # Leakage is included in dynamic when macro is enabled self.lib.write(" leakage_power () {\n") self.lib.write(" when : \"{0}\";\n".format(control_str)) - self.lib.write(" value : {};\n".format(self.char_results["leakage_power"])) + self.lib.write(" value : {};\n".format(self.char_sram_results["leakage_power"])) self.lib.write(" }\n") self.lib.write(" cell_leakage_power : {};\n".format(0)) @@ -298,12 +283,12 @@ class lib: self.lib.write(" }\n\n") - def write_FF_setuphold(self): + def write_FF_setuphold(self, port): """ Adds Setup and Hold timing results""" self.lib.write(" timing(){ \n") self.lib.write(" timing_type : setup_rising; \n") - self.lib.write(" related_pin : \"clk\"; \n") + self.lib.write(" related_pin : \"clk{0}\"; \n".format(port)) self.lib.write(" rise_constraint(CONSTRAINT_TABLE) {\n") rounded_values = list(map(round_time,self.times["setup_times_LH"])) self.write_values(rounded_values,len(self.slews)," ") @@ -315,7 +300,7 @@ class lib: self.lib.write(" }\n") self.lib.write(" timing(){ \n") self.lib.write(" timing_type : hold_rising; \n") - self.lib.write(" related_pin : \"clk\"; \n") + self.lib.write(" related_pin : \"clk{0}\"; \n".format(port)) self.lib.write(" rise_constraint(CONSTRAINT_TABLE) {\n") rounded_values = list(map(round_time,self.times["hold_times_LH"])) self.write_values(rounded_values,len(self.slews)," ") @@ -340,30 +325,29 @@ class lib: self.lib.write(" }\n") - self.lib.write(" pin(DOUT{1}[{0}:0]){{\n".format(self.sram.word_size - 1, read_port)) - self.write_FF_setuphold() + self.lib.write(" pin(DOUT{}){{\n".format(read_port)) self.lib.write(" timing(){ \n") self.lib.write(" timing_sense : non_unate; \n") - self.lib.write(" related_pin : \"clk\"; \n") - self.lib.write(" timing_type : rising_edge; \n") + self.lib.write(" related_pin : \"clk{0}\"; \n".format(read_port)) + self.lib.write(" timing_type : falling_edge; \n") self.lib.write(" cell_rise(CELL_TABLE) {\n") - self.write_values(self.char_results["delay_lh{0}".format(read_port)],len(self.loads)," ") + self.write_values(self.char_port_results[read_port]["delay_lh"],len(self.loads)," ") self.lib.write(" }\n") # rise delay self.lib.write(" cell_fall(CELL_TABLE) {\n") - self.write_values(self.char_results["delay_hl{0}".format(read_port)],len(self.loads)," ") + self.write_values(self.char_port_results[read_port]["delay_hl"],len(self.loads)," ") self.lib.write(" }\n") # fall delay self.lib.write(" rise_transition(CELL_TABLE) {\n") - self.write_values(self.char_results["slew_lh{0}".format(read_port)],len(self.loads)," ") + self.write_values(self.char_port_results[read_port]["slew_lh"],len(self.loads)," ") self.lib.write(" }\n") # rise trans self.lib.write(" fall_transition(CELL_TABLE) {\n") - self.write_values(self.char_results["slew_hl{0}".format(read_port)],len(self.loads)," ") + self.write_values(self.char_port_results[read_port]["slew_hl"],len(self.loads)," ") self.lib.write(" }\n") # fall trans self.lib.write(" }\n") # timing self.lib.write(" }\n") # pin self.lib.write(" }\n\n") # bus def write_data_bus_input(self, write_port): - """ Adds data bus timing results.""" + """ Adds DIN data bus timing results.""" self.lib.write(" bus(DIN{0}){{\n".format(write_port)) self.lib.write(" bus_type : DATA; \n") @@ -372,9 +356,12 @@ class lib: self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"])) self.lib.write(" memory_write(){ \n") self.lib.write(" address : ADDR{0}; \n".format(write_port)) - self.lib.write(" clocked_on : clk; \n") - self.lib.write(" }\n") - self.lib.write(" }\n") + self.lib.write(" clocked_on : clk{0}; \n".format(write_port)) + self.lib.write(" }\n") + self.lib.write(" pin(DIN{}){{\n".format(write_port)) + self.write_FF_setuphold(write_port) + self.lib.write(" }\n") # pin + self.lib.write(" }\n") #bus def write_data_bus(self, port): """ Adds data bus timing results.""" @@ -391,10 +378,10 @@ class lib: self.lib.write(" direction : input; \n") self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"])) self.lib.write(" max_transition : {0};\n".format(self.slews[-1])) - self.lib.write(" pin(ADDR{1}[{0}:0])".format(self.sram.addr_size - 1, port)) + self.lib.write(" pin(ADDR{})".format(port)) self.lib.write("{\n") - self.write_FF_setuphold() + self.write_FF_setuphold(port) self.lib.write(" }\n") self.lib.write(" }\n\n") @@ -403,7 +390,7 @@ class lib: """ Adds control pins timing results.""" #The control pins are still to be determined. This is a placeholder for what could be. ctrl_pin_names = ["CSb{0}".format(port)] - if port in self.write_ports and port in self.read_ports: + if port in self.readwrite_ports: ctrl_pin_names.append("WEb{0}".format(port)) for i in ctrl_pin_names: @@ -411,28 +398,25 @@ class lib: self.lib.write("{\n") self.lib.write(" direction : input; \n") self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"])) - self.write_FF_setuphold() + self.write_FF_setuphold(port) self.lib.write(" }\n\n") - def write_clk_timing_power(self): + def write_clk_timing_power(self, port): """ Adds clk pin timing results.""" - self.lib.write(" pin(clk){\n") + self.lib.write(" pin(clk{0}){{\n".format(port)) self.lib.write(" clock : true;\n") self.lib.write(" direction : input; \n") # FIXME: This depends on the clock buffer size in the control logic self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"])) - #Add power values for the ports. lib generated with this is not syntactically correct. TODO once - #top level is done. - for port in range(self.total_port_num): - self.add_clk_control_power(port) + self.add_clk_control_power(port) - min_pulse_width = round_time(self.char_results["min_period"])/2.0 - min_period = round_time(self.char_results["min_period"]) + min_pulse_width = round_time(self.char_sram_results["min_period"])/2.0 + min_period = round_time(self.char_sram_results["min_period"]) self.lib.write(" timing(){ \n") self.lib.write(" timing_type :\"min_pulse_width\"; \n") - self.lib.write(" related_pin : clk; \n") + self.lib.write(" related_pin : clk{0}; \n".format(port)) self.lib.write(" rise_constraint(scalar) {\n") self.lib.write(" values(\"{0}\"); \n".format(min_pulse_width)) self.lib.write(" }\n") @@ -442,7 +426,7 @@ class lib: self.lib.write(" }\n") self.lib.write(" timing(){ \n") self.lib.write(" timing_type :\"minimum_period\"; \n") - self.lib.write(" related_pin : clk; \n") + self.lib.write(" related_pin : clk{0}; \n".format(port)) self.lib.write(" rise_constraint(scalar) {\n") self.lib.write(" values(\"{0}\"); \n".format(min_period)) self.lib.write(" }\n") @@ -450,8 +434,7 @@ class lib: self.lib.write(" values(\"{0}\"); \n".format(min_period)) self.lib.write(" }\n") self.lib.write(" }\n") - self.lib.write(" }\n") - self.lib.write(" }\n") + self.lib.write(" }\n\n") def add_clk_control_power(self, port): """Writes powers under the clock pin group for a specified port""" @@ -461,9 +444,9 @@ class lib: if port in self.write_ports: if port in self.read_ports: web_name = " & !WEb{0}".format(port) - avg_write_power = np.mean(self.char_results["write1_power{0}".format(port)] + self.char_results["write0_power{0}".format(port)]) + avg_write_power = np.mean(self.char_port_results[port]["write1_power"] + self.char_port_results[port]["write0_power"]) self.lib.write(" internal_power(){\n") - self.lib.write(" when : \"!CSb{0} & clk{1}\"; \n".format(port, web_name)) + self.lib.write(" when : \"!CSb{0} & clk{0}{1}\"; \n".format(port, web_name)) self.lib.write(" rise_power(scalar){\n") self.lib.write(" values(\"{0}\");\n".format(avg_write_power/2.0)) self.lib.write(" }\n") @@ -475,9 +458,9 @@ class lib: if port in self.read_ports: if port in self.write_ports: web_name = " & WEb{0}".format(port) - avg_read_power = np.mean(self.char_results["read1_power{0}".format(port)] + self.char_results["read0_power{0}".format(port)]) + avg_read_power = np.mean(self.char_port_results[port]["read1_power"] + self.char_port_results[port]["read0_power"]) self.lib.write(" internal_power(){\n") - self.lib.write(" when : \"!CSb{0} & !clk{1}\"; \n".format(port, web_name)) + self.lib.write(" when : \"!CSb{0} & !clk{0}{1}\"; \n".format(port, web_name)) self.lib.write(" rise_power(scalar){\n") self.lib.write(" values(\"{0}\");\n".format(avg_read_power/2.0)) self.lib.write(" }\n") @@ -502,13 +485,14 @@ class lib: if not hasattr(self,"d"): self.d = delay(self.sram, self.sp_file, self.corner) if self.use_model: - self.char_results = self.d.analytical_delay(self.sram,self.slews,self.loads) + char_results = self.d.analytical_delay(self.slews,self.loads) + self.char_sram_results, self.char_port_results = char_results else: probe_address = "1" * self.sram.addr_size probe_data = self.sram.word_size - 1 - self.char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads) - - + char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads) + self.char_sram_results, self.char_port_results = char_results + def compute_setup_hold(self): """ Do the analysis if we haven't characterized a FF yet """ # Do the analysis if we haven't characterized a FF yet @@ -519,3 +503,158 @@ class lib: else: self.times = self.sh.analyze(self.slews,self.slews) + + def parse_info(self,corner,lib_name): + """ Copies important characterization data to datasheet.info to be added to datasheet """ + if OPTS.is_unit_test: + git_id = 'AAAAAAAAAAAAAAAAAAAA' + else: + with open(os.devnull, 'wb') as devnull: + proc = subprocess.Popen(['git','rev-parse','HEAD'], cwd=os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/', stdout=subprocess.PIPE) + + git_id = str(proc.stdout.read()) + + try: + git_id = git_id[2:-3] + except: + pass + + if len(git_id) != 40: + debug.warning("Failed to retrieve git id") + git_id = 'Failed to retruieve' + + datasheet = open(OPTS.openram_temp +'/datasheet.info', 'a+') + + current_time = datetime.datetime.now() + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},".format( + "sram_{0}_{1}_{2}".format(OPTS.word_size, OPTS.num_words, OPTS.tech_name), + OPTS.num_words, + OPTS.num_banks, + OPTS.num_rw_ports, + OPTS.num_w_ports, + OPTS.num_r_ports, + OPTS.tech_name, + corner[2], + corner[1], + corner[0], + round_time(self.char_sram_results["min_period"]), + self.out_dir, + lib_name, + OPTS.word_size, + git_id, + current_time + )) + + # information of checks + from hierarchy_design import total_drc_errors + from hierarchy_design import total_lvs_errors + DRC = 'skipped' + LVS = 'skipped' + if OPTS.check_lvsdrc: + DRC = str(total_drc_errors) + LVS = str(total_lvs_errors) + + datasheet.write("{0},{1},".format(DRC, LVS)) + + for port in self.all_ports: + #DIN timings + if port in self.write_ports: + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( + "DIN{1}[{0}:0]".format(self.sram.word_size - 1, port), + min(list(map(round_time,self.times["setup_times_LH"]))), + max(list(map(round_time,self.times["setup_times_LH"]))), + + min(list(map(round_time,self.times["setup_times_HL"]))), + max(list(map(round_time,self.times["setup_times_HL"]))), + + min(list(map(round_time,self.times["hold_times_LH"]))), + max(list(map(round_time,self.times["hold_times_LH"]))), + + min(list(map(round_time,self.times["hold_times_HL"]))), + max(list(map(round_time,self.times["hold_times_HL"]))) + + )) + + for port in self.all_ports: + #DOUT timing + if port in self.read_ports: + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( + "DOUT{1}[{0}:0]".format(self.sram.word_size - 1, port), + min(list(map(round_time,self.char_port_results[port]["delay_lh"]))), + max(list(map(round_time,self.char_port_results[port]["delay_lh"]))), + + min(list(map(round_time,self.char_port_results[port]["delay_hl"]))), + max(list(map(round_time,self.char_port_results[port]["delay_hl"]))), + + min(list(map(round_time,self.char_port_results[port]["slew_lh"]))), + max(list(map(round_time,self.char_port_results[port]["slew_lh"]))), + + min(list(map(round_time,self.char_port_results[port]["slew_hl"]))), + max(list(map(round_time,self.char_port_results[port]["slew_hl"]))) + + + )) + + for port in self.all_ports: + #CSb timings + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( + "CSb{0}".format(port), + min(list(map(round_time,self.times["setup_times_LH"]))), + max(list(map(round_time,self.times["setup_times_LH"]))), + + min(list(map(round_time,self.times["setup_times_HL"]))), + max(list(map(round_time,self.times["setup_times_HL"]))), + + min(list(map(round_time,self.times["hold_times_LH"]))), + max(list(map(round_time,self.times["hold_times_LH"]))), + + min(list(map(round_time,self.times["hold_times_HL"]))), + max(list(map(round_time,self.times["hold_times_HL"]))) + + )) + + for port in self.all_ports: + #ADDR timings + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( + "ADDR{1}[{0}:0]".format(self.sram.addr_size - 1, port), + min(list(map(round_time,self.times["setup_times_LH"]))), + max(list(map(round_time,self.times["setup_times_LH"]))), + + min(list(map(round_time,self.times["setup_times_HL"]))), + max(list(map(round_time,self.times["setup_times_HL"]))), + + min(list(map(round_time,self.times["hold_times_LH"]))), + max(list(map(round_time,self.times["hold_times_LH"]))), + + min(list(map(round_time,self.times["hold_times_HL"]))), + max(list(map(round_time,self.times["hold_times_HL"]))) + + )) + + + for port in self.all_ports: + if port in self.readwrite_ports: + + #WEb timings + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( + "WEb{0}".format(port), + min(list(map(round_time,self.times["setup_times_LH"]))), + max(list(map(round_time,self.times["setup_times_LH"]))), + + min(list(map(round_time,self.times["setup_times_HL"]))), + max(list(map(round_time,self.times["setup_times_HL"]))), + + min(list(map(round_time,self.times["hold_times_LH"]))), + max(list(map(round_time,self.times["hold_times_LH"]))), + + min(list(map(round_time,self.times["hold_times_HL"]))), + max(list(map(round_time,self.times["hold_times_HL"]))) + + )) + + + + + datasheet.write("END\n") + datasheet.close() + diff --git a/compiler/characterizer/logical_effort.py b/compiler/characterizer/logical_effort.py new file mode 100644 index 00000000..bf8c1585 --- /dev/null +++ b/compiler/characterizer/logical_effort.py @@ -0,0 +1,49 @@ +import debug +from tech import drc, parameter, spice + +class logical_effort(): + """ + Class to support the values behind logical effort. Useful for storing the different components + such as logical effort, electrical effort, and parasitic delay. + """ + beta = parameter["beta"] + min_inv_cin = 1+beta + pinv=parameter["min_inv_para_delay"] + + def __init__(self, size, cin, cout, parasitic, out_is_rise=True): + self.cin = cin + self.cout = cout + self.logical_effort = (self.cin/size)/logical_effort.min_inv_cin + self.eletrical_effort = self.cout/self.cin + self.parasitic_scale = parasitic + self.is_rise = out_is_rise + + def __str__(self): + return "g=" + str(self.logical_effort) + ", h=" + str(self.eletrical_effort) + ", p=" + str(self.parasitic_scale)+"*pinv, rise_delay="+str(self.is_rise) + + def get_stage_effort(self): + return self.logical_effort*self.eletrical_effort + + def get_parasitic_delay(self, pinv): + return pinv * self.parasitic_scale + + def get_stage_delay(self, pinv): + return self.get_stage_effort()+self.get_parasitic_delay(pinv) + +def calculate_relative_delay(stage_effort_list, pinv=parameter["min_inv_para_delay"]): + """Calculates the total delay of a given delay path made of a list of logical effort objects.""" + total_rise_delay, total_fall_delay = calculate_relative_rise_fall_delays(stage_effort_list, pinv) + return total_rise_delay + total_fall_delay + +def calculate_relative_rise_fall_delays(stage_effort_list, pinv=parameter["min_inv_para_delay"]): + """Calculates the rise/fall delays of a given delay path made of a list of logical effort objects.""" + debug.info(2, "Calculating rise/fall relative delays") + total_rise_delay, total_fall_delay = 0,0 + for stage in stage_effort_list: + debug.info(3, stage) + if stage.is_rise: + total_rise_delay += stage.get_stage_delay(pinv) + else: + total_fall_delay += stage.get_stage_delay(pinv) + return total_rise_delay, total_fall_delay + \ No newline at end of file diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py new file mode 100644 index 00000000..beca0502 --- /dev/null +++ b/compiler/characterizer/simulation.py @@ -0,0 +1,225 @@ +import sys,re,shutil +from design import design +import debug +import math +import tech +from .stimuli import * +from .trim_spice import * +from .charutils import * +import utils +from globals import OPTS + +class simulation(): + + def __init__(self, sram, spfile, corner): + self.sram = sram + + self.name = self.sram.name + self.word_size = self.sram.word_size + self.addr_size = self.sram.addr_size + self.num_cols = self.sram.num_cols + self.num_rows = self.sram.num_rows + self.num_banks = self.sram.num_banks + self.sp_file = spfile + + self.all_ports = self.sram.all_ports + self.readwrite_ports = self.sram.readwrite_ports + self.read_ports = self.sram.read_ports + self.write_ports = self.sram.write_ports + + def set_corner(self,corner): + """ Set the corner values """ + self.corner = corner + (self.process, self.vdd_voltage, self.temperature) = corner + + def set_spice_constants(self): + """ sets feasible timing parameters """ + self.period = tech.spice["feasible_period"] + self.slew = tech.spice["rise_time"]*2 + self.load = tech.spice["msflop_in_cap"]*4 + + self.v_high = self.vdd_voltage - tech.spice["v_threshold_typical"] + self.v_low = tech.spice["v_threshold_typical"] + self.gnd_voltage = 0 + + def set_stimulus_variables(self): + # Clock signals + self.cycle_times = [] + self.t_current = 0 + + # control signals: only one cs_b for entire multiported sram, one we_b for each write port + self.csb_values = [[] for port in self.all_ports] + self.web_values = [[] for port in self.readwrite_ports] + + # Three dimensional list to handle each addr and data bits for wach port over the number of checks + self.addr_values = [[[] for bit in range(self.addr_size)] for port in self.all_ports] + self.data_values = [[[] for bit in range(self.word_size)] for port in self.write_ports] + + # For generating comments in SPICE stimulus + self.cycle_comments = [] + self.fn_cycle_comments = [] + + def add_control_one_port(self, port, op): + """Appends control signals for operation to a given port""" + #Determine values to write to port + web_val = 1 + csb_val = 1 + if op == "read": + csb_val = 0 + elif op == "write": + csb_val = 0 + web_val = 0 + elif op != "noop": + debug.error("Could not add control signals for port {0}. Command {1} not recognized".format(port,op),1) + + # Append the values depending on the type of port + self.csb_values[port].append(csb_val) + # If port is in both lists, add rw control signal. Condition indicates its a RW port. + if port in self.readwrite_ports: + self.web_values[port].append(web_val) + + def add_data(self, data, port): + """ Add the array of data values """ + debug.check(len(data)==self.word_size, "Invalid data word size.") + + bit = self.word_size - 1 + for c in data: + if c=="0": + self.data_values[port][bit].append(0) + elif c=="1": + self.data_values[port][bit].append(1) + else: + debug.error("Non-binary data string",1) + bit -= 1 + + def add_address(self, address, port): + """ Add the array of address values """ + debug.check(len(address)==self.addr_size, "Invalid address size.") + + bit = self.addr_size - 1 + for c in address: + if c=="0": + self.addr_values[port][bit].append(0) + elif c=="1": + self.addr_values[port][bit].append(1) + else: + debug.error("Non-binary address string",1) + bit -= 1 + + def add_write(self, comment, address, data, port): + """ Add the control values for a write cycle. """ + debug.check(port in self.write_ports, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_ports)) + debug.info(2, comment) + self.fn_cycle_comments.append(comment) + self.append_cycle_comment(port, comment) + + self.cycle_times.append(self.t_current) + self.t_current += self.period + + self.add_control_one_port(port, "write") + self.add_data(data,port) + self.add_address(address,port) + + #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port + noop_data = "0"*self.word_size + #Add noops to all other ports. + for unselected_port in self.all_ports: + if unselected_port != port: + self.add_noop_one_port(address, noop_data, unselected_port) + + def add_read(self, comment, address, din_data, port): + """ Add the control values for a read cycle. """ + debug.check(port in self.read_ports, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_ports)) + debug.info(2, comment) + self.fn_cycle_comments.append(comment) + self.append_cycle_comment(port, comment) + + self.cycle_times.append(self.t_current) + self.t_current += self.period + self.add_control_one_port(port, "read") + + #If the port is also a readwrite then add data. + if port in self.write_ports: + self.add_data(din_data,port) + self.add_address(address, port) + + #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port + noop_data = "0"*self.word_size + #Add noops to all other ports. + for unselected_port in self.all_ports: + if unselected_port != port: + self.add_noop_one_port(address, noop_data, unselected_port) + + def add_noop_all_ports(self, comment, address, data): + """ Add the control values for a noop to all ports. """ + debug.info(2, comment) + self.fn_cycle_comments.append(comment) + self.append_cycle_comment("All", comment) + + self.cycle_times.append(self.t_current) + self.t_current += self.period + + for port in self.all_ports: + self.add_noop_one_port(address, data, port) + + def add_write_one_port(self, comment, address, data, port): + """ Add the control values for a write cycle. Does not increment the period. """ + debug.check(port in self.write_ports, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_ports)) + debug.info(2, comment) + self.fn_cycle_comments.append(comment) + + self.add_control_one_port(port, "write") + self.add_data(data,port) + self.add_address(address,port) + + def add_read_one_port(self, comment, address, din_data, port): + """ Add the control values for a read cycle. Does not increment the period. """ + debug.check(port in self.read_ports, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_ports)) + debug.info(2, comment) + self.fn_cycle_comments.append(comment) + + self.add_control_one_port(port, "read") + #If the port is also a readwrite then add data. + if port in self.write_ports: + self.add_data(din_data,port) + self.add_address(address, port) + + def add_noop_one_port(self, address, data, port): + """ Add the control values for a noop to a single port. Does not increment the period. """ + self.add_control_one_port(port, "noop") + if port in self.write_ports: + self.add_data(data,port) + self.add_address(address, port) + + def append_cycle_comment(self, port, comment): + """Add comment to list to be printed in stimulus file""" + #Clean up time before appending. Make spacing dynamic as well. + time = "{0:.2f} ns:".format(self.t_current) + time_spacing = len(time)+6 + self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times), + port, + time, + time_spacing, + comment)) + + def gen_cycle_comment(self, op, word, addr, port, t_current): + if op == "noop": + comment = "\tIdle during cycle {0} ({1}ns - {2}ns)".format(int(t_current/self.period), + t_current, + t_current+self.period) + elif op == "write": + comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word, + addr, + port, + int(t_current/self.period), + t_current, + t_current+self.period) + else: + comment = "\tReading {0} from address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word, + addr, + port, + int(t_current/self.period), + t_current, + t_current+self.period) + return comment + diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index b99aadd4..8c6b059f 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -28,39 +28,53 @@ class stimuli(): (self.process, self.voltage, self.temperature) = corner self.device_models = tech.spice["fet_models"][self.process] - - def inst_sram(self, abits, dbits, port_info, sram_name): + def inst_sram(self, sram, port_signal_names, port_info, abits, dbits, sram_name): """ Function to instatiate an SRAM subckt. """ + pin_names = self.gen_pin_names(port_signal_names, port_info, abits, dbits) + #Only checking length. This should check functionality as well (TODO) and/or import that information from the SRAM + debug.check(len(sram.pins) == len(pin_names), "Number of pins generated for characterization do match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(sram.pins,pin_names)) + self.sf.write("Xsram ") - - #Un-tuple the port names. This was done to avoid passing them all as arguments. Could be improved still. - #This should be generated from the pin list of the sram... change when multiport pins done. - (total_port_num,readwrite_num,read_ports,write_ports) = port_info - - for write_input in write_ports: - for i in range(dbits): - self.sf.write("DIN{0}[{1}] ".format(write_input, i)) - - for port in range(total_port_num): - for i in range(abits): - self.sf.write("A{0}[{1}] ".format(port,i)) - - #These control signals assume 6t sram i.e. a single readwrite port. If multiple readwrite ports are used then add more - #control signals. Not sure if this is correct, consider a temporary change until control signals for multiport are finalized. - for port in range(total_port_num): - self.sf.write("CSB{0} ".format(port)) - for readwrite_port in range(readwrite_num): - self.sf.write("WEB{0} ".format(readwrite_port)) - - self.sf.write("{0} ".format(tech.spice["clk"])) - for read_output in read_ports: - for i in range(dbits): - self.sf.write("DOUT{0}[{1}] ".format(read_output, i)) - self.sf.write("{0} {1} ".format(self.vdd_name, self.gnd_name)) + for pin in pin_names: + self.sf.write("{0} ".format(pin)) self.sf.write("{0}\n".format(sram_name)) + def gen_pin_names(self, port_signal_names, port_info, abits, dbits): + """Creates the pins names of the SRAM based on the no. of ports.""" + #This may seem redundant as the pin names are already defined in the sram. However, it is difficult to extract the + #functionality from the names, so they are recreated. As the order is static, changing the order of the pin names + #will cause issues here. + pin_names = [] + (addr_name, din_name, dout_name) = port_signal_names + (total_ports, write_index, read_index) = port_info + + for write_input in write_index: + for i in range(dbits): + pin_names.append("{0}{1}_{2}".format(din_name,write_input, i)) + + for port in range(total_ports): + for i in range(abits): + pin_names.append("{0}{1}_{2}".format(addr_name,port,i)) + #Control signals not finalized. + for port in range(total_ports): + pin_names.append("CSB{0}".format(port)) + for port in range(total_ports): + if (port in read_index) and (port in write_index): + pin_names.append("WEB{0}".format(port)) + + for port in range(total_ports): + pin_names.append("{0}{1}".format(tech.spice["clk"], port)) + + for read_output in read_index: + for i in range(dbits): + pin_names.append("{0}{1}_{2}".format(dout_name,read_output, i)) + + pin_names.append("{0}".format(self.vdd_name)) + pin_names.append("{0}".format(self.gnd_name)) + return pin_names + def inst_model(self, pins, model_name): """ Function to instantiate a generic model with a set of pins """ self.sf.write("X{0} ".format(model_name)) @@ -153,7 +167,7 @@ class stimuli(): to the initial value. """ # the initial value is not a clock time - debug.check(len(clk_times)==len(data_values),"Clock and data value lengths don't match.") + debug.check(len(clk_times)==len(data_values),"Clock and data value lengths don't match. {0} clock values, {1} data values for {2}".format(len(clk_times), len(data_values), sig_name)) # shift signal times earlier for setup time times = np.array(clk_times) - setup*period @@ -202,6 +216,16 @@ class stimuli(): targ_dir, targ_td)) + def gen_meas_find_voltage(self, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td): + """ Creates the .meas statement for the measurement of delay """ + measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n" + self.sf.write(measure_string.format(meas_name, + targ_name, + trig_name, + trig_val, + trig_dir, + trig_td)) + def gen_meas_power(self, meas_name, t_initial, t_final): """ Creates the .meas statement for the measurement of avg power """ # power mea cmd is different in different spice: @@ -213,19 +237,35 @@ class stimuli(): power_exp, t_initial, t_final)) + + def gen_meas_value(self, meas_name, dout, t_intital, t_final): + measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name, dout, t_intital, t_final) + self.sf.write(measure_string) - def write_control(self, end_time): + def write_control(self, end_time, runlvl=4): """ Write the control cards to run and end the simulation """ + + # These are guesses... + if runlvl==1: + reltol = 0.02 # 2% + elif runlvl==2: + reltol = 0.01 # 1% + elif runlvl==3: + reltol = 0.005 # 0.5% + else: + reltol = 0.001 # 0.1% + timestep = 10 #ps, was 5ps but ngspice was complaining the timestep was too small in certain tests. + # UIC is needed for ngspice to converge - self.sf.write(".TRAN 5p {0}n UIC\n".format(end_time)) + self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep,end_time)) if OPTS.spice_name == "ngspice": # ngspice sometimes has convergence problems if not using gear method # which is more accurate, but slower than the default trapezoid method # Do not remove this or it may not converge due to some "pa_00" nodes # unless you figure out what these are. - self.sf.write(".OPTIONS POST=1 RUNLVL=4 PROBE method=gear TEMP={}\n".format(self.temperature)) + self.sf.write(".OPTIONS POST=1 RELTOL={0} PROBE method=gear TEMP={1}\n".format(reltol,self.temperature)) else: - self.sf.write(".OPTIONS POST=1 RUNLVL=4 PROBE TEMP={}\n".format(self.temperature)) + self.sf.write(".OPTIONS POST=1 RUNLVL={0} PROBE TEMP={1}\n".format(runlvl,self.temperature)) # create plots for all signals self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n") @@ -255,12 +295,15 @@ class stimuli(): def write_supply(self): """ Writes supply voltage statements """ - self.sf.write("V{0} {0} 0.0 {1}\n".format(self.vdd_name, self.voltage)) - self.sf.write("V{0} {0} 0.0 {1}\n".format(self.gnd_name, 0)) + gnd_node_name = "0" + self.sf.write("V{0} {0} {1} {2}\n".format(self.vdd_name, gnd_node_name, self.voltage)) # This is for the test power supply - self.sf.write("V{0} {0} 0.0 {1}\n".format("test"+self.vdd_name, self.voltage)) - self.sf.write("V{0} {0} 0.0 {1}\n".format("test"+self.gnd_name, 0)) + self.sf.write("V{0} {0} {1} {2}\n".format("test"+self.vdd_name, gnd_node_name, self.voltage)) + self.sf.write("V{0} {0} {1} {2}\n".format("test"+self.gnd_name, gnd_node_name, 0.0)) + #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") + self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0)) def run_sim(self): """ Run hspice in batch mode and output rawfile to parse. """ diff --git a/compiler/characterizer/trim_spice.py b/compiler/characterizer/trim_spice.py index c3a6ee69..88195fee 100644 --- a/compiler/characterizer/trim_spice.py +++ b/compiler/characterizer/trim_spice.py @@ -1,5 +1,6 @@ import debug from math import log +import re class trim_spice(): """ @@ -49,13 +50,13 @@ class trim_spice(): # Split up the address and convert to an int wl_address = int(address[self.col_addr_size:],2) - if self.col_addr_size>1: + if self.col_addr_size>0: col_address = int(address[0:self.col_addr_size],2) else: 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(int(self.words_per_row*data_bit + col_address)) + wl_name = "wl_{}".format(wl_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) @@ -73,25 +74,28 @@ class trim_spice(): self.sp_buffer.insert(0, "* It should NOT be used for LVS!!") self.sp_buffer.insert(0, "* WARNING: This is a TRIMMED NETLIST.") - self.remove_insts("bitcell_array",[wl_name,bl_name]) + + wl_regex = r"wl\d*_{}".format(wl_address) + bl_regex = r"bl\d*_{}".format(int(self.words_per_row*data_bit + col_address)) + self.remove_insts("bitcell_array",[wl_regex,bl_regex]) # 2. Keep sense amps basd on BL # FIXME: The bit lines are not indexed the same in sense_amp_array - #self.remove_insts("sense_amp_array",[bl_name]) + #self.remove_insts("sense_amp_array",[bl_regex]) # 3. Keep column muxes basd on BL - self.remove_insts("column_mux_array",[bl_name]) + self.remove_insts("column_mux_array",[bl_regex]) # 4. Keep write driver based on DATA - data_name = "data[{}]".format(data_bit) - self.remove_insts("write_driver_array",[data_name]) + data_regex = r"data_{}".format(data_bit) + self.remove_insts("write_driver_array",[data_regex]) # 5. Keep wordline driver based on WL # Need to keep the gater too - #self.remove_insts("wordline_driver",wl_name) + #self.remove_insts("wordline_driver",wl_regex) # 6. Keep precharges based on BL - self.remove_insts("precharge_array",[bl_name]) + self.remove_insts("precharge_array",[bl_regex]) # Everything else isn't worth removing. :) @@ -107,6 +111,10 @@ class trim_spice(): match of the line with a term so you can search for a single net connection, the instance name, anything.. """ + removed_insts = 0 + #Expects keep_inst_list are regex patterns. Compile them here. + compiled_patterns = [re.compile(pattern) for pattern in keep_inst_list] + start_name = ".SUBCKT {}".format(subckt_name) end_name = ".ENDS {}".format(subckt_name) @@ -120,11 +128,14 @@ class trim_spice(): new_buffer.append(line) in_subckt=False elif in_subckt: - for k in keep_inst_list: - if k in line: + removed_insts += 1 + for pattern in compiled_patterns: + if pattern.search(line) != None: new_buffer.append(line) + removed_insts -= 1 break else: new_buffer.append(line) self.sp_buffer = new_buffer + debug.info(2, "Removed {} instances from {} subcircuit.".format(removed_insts, subckt_name)) diff --git a/compiler/characterizer/worst_case.py b/compiler/characterizer/worst_case.py new file mode 100644 index 00000000..6dad95d9 --- /dev/null +++ b/compiler/characterizer/worst_case.py @@ -0,0 +1,77 @@ +import sys,re,shutil +import debug +import tech +import math +from .stimuli import * +from .trim_spice import * +from .charutils import * +import utils +from globals import OPTS +from .delay import delay + +class worst_case(delay): + """Functions to test for the worst case delay in a target SRAM + + The current worst case determines a feasible period for the SRAM then tests + several bits and record the delay and differences between the bits. + + """ + + def __init__(self, sram, spfile, corner): + delay.__init__(self,sram,spfile,corner) + + + def analyze(self,probe_address, probe_data, slews, loads): + """ + Main function to test the delays of different bits. + """ + debug.check(OPTS.num_rw_ports < 2 and OPTS.num_w_ports < 1 and OPTS.num_r_ports < 1 , + "Bit testing does not currently support multiport.") + #Dict to hold all characterization values + char_sram_data = {} + + self.set_probe(probe_address, probe_data) + #self.prepare_netlist() + + self.load=max(loads) + self.slew=max(slews) + + # 1) Find a feasible period and it's corresponding delays using the trimmed array. + feasible_delays = self.find_feasible_period() + + # 2) Find the delays of several bits + test_bits = self.get_test_bits() + bit_delays = self.simulate_for_bit_delays(test_bits) + + for i in range(len(test_bits)): + debug.info(1, "Bit tested: addr {0[0]} data_pos {0[1]}\n Values {1}".format(test_bits[i], bit_delays[i])) + + def simulate_for_bit_delays(self, test_bits): + """Simulates the delay of the sram of over several bits.""" + bit_delays = [{} for i in range(len(test_bits))] + + #Assumes a bitcell with only 1 rw port. (6t, port 0) + port = 0 + self.targ_read_ports = [self.read_ports[port]] + self.targ_write_ports = [self.write_ports[port]] + + for i in range(len(test_bits)): + (bit_addr, bit_data) = test_bits[i] + self.set_probe(bit_addr, bit_data) + debug.info(1,"Delay bit test: period {}, addr {}, data_pos {}".format(self.period, bit_addr, bit_data)) + (success, results)=self.run_delay_simulation() + debug.check(success, "Bit Test Failed: period {}, addr {}, data_pos {}".format(self.period, bit_addr, bit_data)) + bit_delays[i] = results[port] + + return bit_delays + + + def get_test_bits(self): + """Statically determines address and bit values to test""" + #First and last address, first middle, and last bit. Last bit is repeated twice with different data position. + bit_addrs = ["0"*self.addr_size, "0"+"1"*(self.addr_size-1), "1"*self.addr_size, "1"*self.addr_size] + data_positions = [0, (self.word_size-1)//2, 0, self.word_size-1] + #Return them in a tuple form + return [(bit_addrs[i], data_positions[i]) for i in range(len(bit_addrs))] + + diff --git a/compiler/datasheet/assets/datasheet.css b/compiler/datasheet/assets/datasheet.css new file mode 100644 index 00000000..ff16f101 --- /dev/null +++ b/compiler/datasheet/assets/datasheet.css @@ -0,0 +1,28 @@ + + diff --git a/compiler/datasheet/assets/openram_logo_placeholder.png b/compiler/datasheet/assets/openram_logo_placeholder.png new file mode 100644 index 00000000..b19f0bfe Binary files /dev/null and b/compiler/datasheet/assets/openram_logo_placeholder.png differ diff --git a/compiler/datasheet/assets/vlsi_logo.png b/compiler/datasheet/assets/vlsi_logo.png new file mode 100644 index 00000000..3f02a45e Binary files /dev/null and b/compiler/datasheet/assets/vlsi_logo.png differ diff --git a/compiler/datasheet/datasheet.py b/compiler/datasheet/datasheet.py new file mode 100644 index 00000000..ce84c22c --- /dev/null +++ b/compiler/datasheet/datasheet.py @@ -0,0 +1,76 @@ +from table_gen import * +import os +import csv +import base64 +from globals import OPTS + +class datasheet(): + """ + Defines the layout,but not the data, of the html datasheet + """ + def __init__(self,identifier): + self.name = identifier + self.html = "" + + + def generate_html(self): + """ + Generates html tables using flask-table + """ + with open(os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/datasheet/assets/datasheet.css', 'r') as datasheet_css: + #css styling is kept in a seperate file + self.html += datasheet_css.read() + + +# with open(OPTS.openram_temp + "/datasheet.info") as info: + self.html += '' + + vlsi_logo = 0 + with open(os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/datasheet/assets/vlsi_logo.png' , "rb") as image_file: + vlsi_logo = base64.b64encode(image_file.read()) + + openram_logo = 0 + with open(os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/datasheet/assets/openram_logo_placeholder.png' , "rb") as image_file: + openram_logo = base64.b64encode(image_file.read()) + + + self.html += 'VLSIDA'.format(str(vlsi_logo)[2:-1]) + + + + + + self.html +='

'+ self.name + '.html' + '

' + self.html +='

Compiled at: '+ self.time + '

' + self.html +='

'+ 'DRC errors: ' + str(self.DRC) + '

' + self.html +='

'+ 'LVS errors: ' + str(self.LVS) + '

' + self.html += '

'+ 'Git commit id: ' + str(self.git_id) + '

' + + self.html +='

Ports and Configuration

' +# self.html += in_out(self.io,table_id='data').__html__().replace('<','<').replace('"','"').replace('>',">") + self.html += self.io_table.to_html() + + self.html +='

Operating Conditions

' +# self.html += operating_conditions(self.operating,table_id='data').__html__() + self.html += self.operating_table.to_html() + + self.html += '

Timing and Current Data

' +# self.html += timing_and_current_data(self.timing,table_id='data').__html__() + self.html += self.timing_table.to_html() + + self.html += '

Characterization Corners

' +# self.html += characterization_corners(self.corners,table_id='data').__html__() + self.html += self.corners_table.to_html() + + self.html +='

Deliverables

' +# self.html += deliverables(self.dlv,table_id='data').__html__().replace('<','<').replace('"','"').replace('>',">") + self.html += self.dlv_table.to_html() + + + diff --git a/compiler/datasheet/datasheet_gen.py b/compiler/datasheet/datasheet_gen.py new file mode 100644 index 00000000..db03216b --- /dev/null +++ b/compiler/datasheet/datasheet_gen.py @@ -0,0 +1,530 @@ +#!/usr/bin/env python3 +""" +This is a script to load data from the characterization and layout processes into +a web friendly html datasheet. +""" +#TODO: +#include log file +#Diagram generation +#Improve css + + +import debug +from globals import OPTS +import os, math +import optparse +import csv +from datasheet import * +from table_gen import * + +def process_name(corner): + """ + Expands the names of the characterization corner types into something human friendly + """ + if corner == "TT": + return "Typical - Typical" + if corner == "SS": + return "Slow - Slow" + if corner == "FF": + return "Fast - Fast" + else: + return "custom" + +def parse_characterizer_csv(sram,f,pages): + """ + Parses output data of the Liberty file generator in order to construct the timing and + current table + """ + with open(f) as csv_file: + csv_reader = csv.reader(csv_file, delimiter=',') + line_count = 0 + for row in csv_reader: + + found = 0 + col = 0 + + #defines layout of csv file + NAME = row[col] + col += 1 + + NUM_WORDS = row[col] + col += 1 + + NUM_BANKS = row[col] + col += 1 + + NUM_RW_PORTS = row[col] + col += 1 + + NUM_W_PORTS = row[col] + col += 1 + + NUM_R_PORTS = row[col] + col += 1 + + TECH_NAME = row[col] + col += 1 + + TEMP = row[col] + col += 1 + + VOLT = row[col] + col += 1 + + PROC = row[col] + col += 1 + + MIN_PERIOD = row[col] + col += 1 + + OUT_DIR = row[col] + col += 1 + + LIB_NAME = row[col] + col += 1 + + WORD_SIZE = row[col] + col += 1 + + ORIGIN_ID = row[col] + col += 1 + + DATETIME = row[col] + col+= 1 + + DRC = row[col] + col += 1 + + LVS = row[col] + col += 1 + + for sheet in pages: + + + if sheet.name == NAME: + + found = 1 + #if the .lib information is for an existing datasheet compare timing data + + for item in sheet.operating_table.rows: + #check if the new corner data is worse than the previous worse corner data + + if item[0] == 'Operating Temperature': + if float(TEMP) > float(item[3]): + item[2] = item[3] + item[3] = TEMP + if float(TEMP) < float(item[1]): + item[2] = item[1] + item[1] = TEMP + + if item[0] == 'Power supply (VDD) range': + if float(VOLT) > float(item[3]): + item[2] = item[3] + item[3] = VOLT + if float(VOLT) < float(item[1]): + item[2] = item[1] + item[1] = VOLT + + if item[0] == 'Operating Frequncy (F)': + try: + if float(math.floor(1000/float(MIN_PERIOD)) < float(item[3])): + item[3] = str(math.floor(1000/float(MIN_PERIOD))) + except Exception: + pass + + + while(True): + + + if(row[col].startswith('DIN')): + start = col + for item in sheet.timing_table.rows: + if item[0].startswith(row[col]): + + if item[0].endswith('setup rising'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('setup falling'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('hold rising'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('hold falling'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + col += 1 + + elif(row[col].startswith('DOUT')): + start = col + for item in sheet.timing_table.rows: + if item[0].startswith(row[col]): + + if item[0].endswith('cell rise'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('cell fall'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('rise transition'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('fall transition'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + col += 1 + + elif(row[col].startswith('CSb')): + start = col + for item in sheet.timing_table.rows: + if item[0].startswith(row[col]): + + if item[0].endswith('setup rising'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('setup falling'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('hold rising'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('hold falling'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + col += 1 + + + elif(row[col].startswith('WEb')): + start = col + for item in sheet.timing_table.rows: + if item[0].startswith(row[col]): + + if item[0].endswith('setup rising'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('setup falling'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('hold rising'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('hold falling'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + col += 1 + + + elif(row[col].startswith('ADDR')): + start = col + for item in sheet.timing_table.rows: + if item[0].startswith(row[col]): + + if item[0].endswith('setup rising'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('setup falling'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('hold rising'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + elif item[0].endswith('hold falling'): + if float(row[col+1]) < float(item[1]): + item[1] = row[col+1] + if float(row[col+2]) > float(item[2]): + item[2] = row[col+2] + + col += 2 + + col += 1 + + + + else: + break + + + new_sheet.corners_table.add_row([PROC,process_name(PROC),VOLT,TEMP,LIB_NAME.replace(OUT_DIR,'').replace(NAME,'')]) + new_sheet.dlv_table.add_row(['.lib','Synthesis models','{1}'.format(LIB_NAME,LIB_NAME.replace(OUT_DIR,''))]) + + if found == 0: + + #if this is the first corner for this sram, run first time configuration and set up tables + new_sheet = datasheet(NAME) + pages.append(new_sheet) + + new_sheet.git_id = ORIGIN_ID + new_sheet.time = DATETIME + new_sheet.DRC = DRC + new_sheet.LVS = LVS + new_sheet.description = [NAME, NUM_WORDS, NUM_BANKS, NUM_RW_PORTS, NUM_W_PORTS, NUM_R_PORTS, TECH_NAME, WORD_SIZE, ORIGIN_ID, DATETIME] + + new_sheet.corners_table = table_gen("corners") + new_sheet.corners_table.add_row(['Corner Name','Process','Power Supply','Temperature','Library Name Suffix']) + new_sheet.corners_table.add_row([PROC,process_name(PROC),VOLT,TEMP,LIB_NAME.replace(OUT_DIR,'').replace(NAME,'')]) + new_sheet.operating_table = table_gen("operating_table") + new_sheet.operating_table.add_row(['Parameter','Min','Typ','Max','Units']) + new_sheet.operating_table.add_row(['Power supply (VDD) range',VOLT,VOLT,VOLT,'Volts']) + new_sheet.operating_table.add_row(['Operating Temperature',TEMP,TEMP,TEMP,'Celsius']) + + try: + new_sheet.operating_table.add_row(['Operating Frequency (F)','','',str(math.floor(1000/float(MIN_PERIOD))),'MHz']) + except Exception: + new_sheet.operating_table.add_row(['Operating Frequency (F)','','',"not available in netlist only",'MHz']) #failed to provide non-zero MIN_PERIOD + new_sheet.timing_table = table_gen("timing") + new_sheet.timing_table.add_row(['Parameter','Min','Max','Units']) + while(True): + if(row[col].startswith('DIN')): + start = col + + new_sheet.timing_table.add_row(['{0} setup rising'.format(row[start]),row[col+1],row[col+2],'ns']) + col += 2 + + + new_sheet.timing_table.add_row(['{0} setup falling'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + new_sheet.timing_table.add_row(['{0} hold rising'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + new_sheet.timing_table.add_row(['{0} hold falling'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + col +=1 + + elif(row[col].startswith('DOUT')): + start = col + + new_sheet.timing_table.add_row(['{0} cell rise'.format(row[start]),row[col+1],row[col+2],'ns']) + col += 2 + + + new_sheet.timing_table.add_row(['{0} cell fall'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + new_sheet.timing_table.add_row(['{0} rise transition'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + new_sheet.timing_table.add_row(['{0} fall transition'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + col +=1 + + elif(row[col].startswith('CSb')): + start = col + + new_sheet.timing_table.add_row(['{0} setup rising'.format(row[start]),row[col+1],row[col+2],'ns']) + col += 2 + + + new_sheet.timing_table.add_row(['{0} setup falling'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + new_sheet.timing_table.add_row(['{0} hold rising'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + new_sheet.timing_table.add_row(['{0} hold falling'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + col +=1 + + elif(row[col].startswith('WEb')): + start = col + + new_sheet.timing_table.add_row(['{0} setup rising'.format(row[start]),row[col+1],row[col+2],'ns']) + col += 2 + + + new_sheet.timing_table.add_row(['{0} setup falling'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + new_sheet.timing_table.add_row(['{0} hold rising'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + new_sheet.timing_table.add_row(['{0} hold falling'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + col +=1 + + elif(row[col].startswith('ADDR')): + start = col + + new_sheet.timing_table.add_row(['{0} setup rising'.format(row[start]),row[col+1],row[col+2],'ns']) + col += 2 + + + new_sheet.timing_table.add_row(['{0} setup falling'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + new_sheet.timing_table.add_row(['{0} hold rising'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + new_sheet.timing_table.add_row(['{0} hold falling'.format(row[start]),row[col+1],row[col+2],'ns']) + + col += 2 + + col +=1 + + else: + break + + + + new_sheet.dlv_table = table_gen("dlv") + new_sheet.dlv_table.add_row(['Type','Description','Link']) + + new_sheet.io_table = table_gen("io") + new_sheet.io_table.add_row(['Type', 'Value']) + + if not OPTS.netlist_only: + #physical layout files should not be generated in netlist only mode + new_sheet.dlv_table.add_row(['.gds','GDSII layout views','{0}.{1}'.format(OPTS.output_name,'gds')]) + new_sheet.dlv_table.add_row(['.lef','LEF files','{0}.{1}'.format(OPTS.output_name,'lef')]) + + + new_sheet.dlv_table.add_row(['.sp','SPICE netlists','{0}.{1}'.format(OPTS.output_name,'sp')]) + new_sheet.dlv_table.add_row(['.v','Verilog simulation models','{0}.{1}'.format(OPTS.output_name,'v')]) + new_sheet.dlv_table.add_row(['.html','This datasheet','{0}.{1}'.format(OPTS.output_name,'html')]) + new_sheet.dlv_table.add_row(['.lib','Synthesis models','{1}'.format(LIB_NAME,LIB_NAME.replace(OUT_DIR,''))]) + new_sheet.dlv_table.add_row(['.py','OpenRAM configuration file','{0}.{1}'.format(OPTS.output_name,'py')]) + + new_sheet.io_table.add_row(['WORD_SIZE',WORD_SIZE]) + new_sheet.io_table.add_row(['NUM_WORDS',NUM_WORDS]) + new_sheet.io_table.add_row(['NUM_BANKS',NUM_BANKS]) + new_sheet.io_table.add_row(['NUM_RW_PORTS',NUM_RW_PORTS]) + new_sheet.io_table.add_row(['NUM_R_PORTS',NUM_R_PORTS]) + new_sheet.io_table.add_row(['NUM_W_PORTS',NUM_W_PORTS]) + new_sheet.io_table.add_row(['Area',sram.width * sram.height]) + + + + + + +class datasheet_gen(): + def datasheet_write(sram,name): + + + in_dir = OPTS.openram_temp + + if not (os.path.isdir(in_dir)): + os.mkdir(in_dir) + + + datasheets = [] + parse_characterizer_csv(sram, in_dir + "/datasheet.info", datasheets) + + + for sheets in datasheets: + with open(name, 'w+') as f: + sheets.generate_html() + f.write(sheets.html) diff --git a/compiler/datasheet/table_gen.py b/compiler/datasheet/table_gen.py new file mode 100644 index 00000000..18590739 --- /dev/null +++ b/compiler/datasheet/table_gen.py @@ -0,0 +1,43 @@ +class table_gen: + def __init__(self,name): + self.name = name + self.rows = [] + self.table_id = 'data' + + def add_row(self,row): + self.rows.append(row) + + def gen_table_head(self): + html = '' + + html += '' + html += '' + for col in self.rows[0]: + html += '' + str(col) + '' + html += '' + html += '' + return html + + def gen_table_body(self): + html = '' + + html += '' + html += '' + for row in self.rows[1:]: + html += '' + for col in row: + html += '' + str(col) + '' + html += '' + html += '' + html += '' + return html + + def to_html(self): + + html = '' + html += '' + html += self.gen_table_head() + html += self.gen_table_body() + html += '
' + + return html diff --git a/compiler/datasheet/wavedrom.py b/compiler/datasheet/wavedrom.py new file mode 100644 index 00000000..e8c68c56 --- /dev/null +++ b/compiler/datasheet/wavedrom.py @@ -0,0 +1,827 @@ +#!/usr/bin/python +# The MIT License (MIT) +# +# Copyright (c) 2011-2016 Aliaksei Chapyzhenka +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# Translated to Python from original file: +# https://github.com/drom/wavedrom/blob/master/src/WaveDrom.js +# + +import sys +import json +import math +import waveskin + +font_width = 7 + +lane = { + "xs" : 20, # tmpgraphlane0.width + "ys" : 20, # tmpgraphlane0.height + "xg" : 120, # tmpgraphlane0.x + "yg" : 0, # head gap + "yh0" : 0, # head gap title + "yh1" : 0, # head gap + "yf0" : 0, # foot gap + "yf1" : 0, # foot gap + "y0" : 5, # tmpgraphlane0.y + "yo" : 30, # tmpgraphlane1.y - y0 + "tgo" : -10, # tmptextlane0.x - xg + "ym" : 15, # tmptextlane0.y - y0 + "xlabel" : 6, # tmptextlabel.x - xg + "xmax" : 1, + "scale" : 1, + "head" : {}, + "foot" : {} +} + +def genBrick (texts, extra, times) : + + R = [] + if len( texts ) == 4 : + for j in range( times ): + + R.append(texts[0]) + + for i in range ( extra ): + R.append(texts[1]) + + R.append(texts[2]) + for i in range ( extra ): + R.append(texts[3]) + + return R + + if len( texts ) == 1 : + texts.append(texts[0]) + + R.append(texts[0]) + for i in range (times * (2 * (extra + 1)) - 1) : + R.append(texts[1]) + return R + +def genFirstWaveBrick (text, extra, times) : + + pattern = { + 'p': ['pclk', '111', 'nclk', '000'], + 'n': ['nclk', '000', 'pclk', '111'], + 'P': ['Pclk', '111', 'nclk', '000'], + 'N': ['Nclk', '000', 'pclk', '111'], + 'l': ['000'], + 'L': ['000'], + '0': ['000'], + 'h': ['111'], + 'H': ['111'], + '1': ['111'], + '=': ['vvv-2'], + '2': ['vvv-2'], + '3': ['vvv-3'], + '4': ['vvv-4'], + '5': ['vvv-5'], + 'd': ['ddd'], + 'u': ['uuu'], + 'z': ['zzz'] + } + + return genBrick( pattern.get( text, ['xxx'] ) , extra, times ); + +def genWaveBrick (text, extra, times) : + + x1 = {'p':'pclk', 'n':'nclk', 'P':'Pclk', 'N':'Nclk', 'h':'pclk', 'l':'nclk', 'H':'Pclk', 'L':'Nclk'} + x2 = {'0':'0', '1':'1', 'x':'x', 'd':'d', 'u':'u', 'z':'z', '=':'v', '2':'v', '3':'v', '4':'v', '5':'v' } + x3 = {'0': '', '1': '', 'x': '', 'd': '', 'u': '', 'z': '', '=':'-2', '2':'-2', '3':'-3', '4':'-4', '5':'-5'} + y1 = { + 'p':'0', 'n':'1', + 'P':'0', 'N':'1', + 'h':'1', 'l':'0', + 'H':'1', 'L':'0', + '0':'0', '1':'1', 'x':'x', 'd':'d', 'u':'u', 'z':'z', '=':'v', '2':'v', '3':'v', '4':'v', '5':'v'} + + y2 = { + 'p': '', 'n': '', + 'P': '', 'N': '', + 'h': '', 'l': '', + 'H': '', 'L': '', + '0': '', '1': '', 'x': '', 'd': '', 'u': '', 'z': '', '=':'-2', '2':'-2', '3':'-3', '4':'-4', '5':'-5'} + + x4 = { + 'p': '111', 'n': '000', + 'P': '111', 'N': '000', + 'h': '111', 'l': '000', + 'H': '111', 'L': '000', + '0': '000', '1': '111', 'x': 'xxx', 'd': 'ddd', 'u': 'uuu', 'z': 'zzz', + '=': 'vvv-2', '2': 'vvv-2', '3': 'vvv-3', '4': 'vvv-4', '5': 'vvv-5'} + + x5 = {'p':'nclk', 'n':'pclk', 'P':'nclk', 'N':'pclk'} + x6 = {'p': '000', 'n': '111', 'P': '000', 'N': '111'} + xclude = {'hp':'111', 'Hp':'111', 'ln': '000', 'Ln': '000', 'nh':'111', 'Nh':'111', 'pl': '000', 'Pl':'000'} + + #atext = text.split() + atext = text + + tmp0 = x4.get(atext[1]) + tmp1 = x1.get(atext[1]) + if tmp1 == None : + tmp2 = x2.get(atext[1]) + if tmp2 == None : + # unknown + return genBrick(['xxx'], extra, times) + else : + tmp3 = y1.get(atext[0]) + if tmp3 == None : + # unknown + return genBrick(['xxx'], extra, times) + + # soft curves + return genBrick([tmp3 + 'm' + tmp2 + y2[atext[0]] + x3[atext[1]], tmp0], extra, times) + + else : + tmp4 = xclude.get(text) + if tmp4 != None : + tmp1 = tmp4 + + # sharp curves + tmp2 = x5.get(atext[1]) + if tmp2 == None : + # hlHL + return genBrick([tmp1, tmp0], extra, times) + else : + # pnPN + return genBrick([tmp1, tmp0, tmp2, x6[atext[1]]], extra, times) + +def parseWaveLane (text, extra) : + + R = [] + Stack = text + Next = Stack[0] + Stack = Stack[1:] + + Repeats = 1 + while len(Stack) and ( Stack[0] == '.' or Stack[0] == '|' ): # repeaters parser + Stack=Stack[1:] + Repeats += 1 + + R.extend(genFirstWaveBrick(Next, extra, Repeats)) + + while len(Stack) : + Top = Next + Next = Stack[0] + Stack = Stack[1:] + Repeats = 1 + while len(Stack) and ( Stack[0] == '.' or Stack[0] == '|' ) : # repeaters parser + Stack=Stack[1:] + Repeats += 1 + R.extend(genWaveBrick((Top + Next), extra, Repeats)) + + for i in range( lane['phase'] ): + R = R[1:] + return R + +def parseWaveLanes (sig) : + + def data_extract (e) : + tmp = e.get('data') + if tmp == None : return None + if is_type_str (tmp) : tmp=tmp.split() + return tmp + + content = [] + for sigx in sig : + lane['period'] = sigx.get('period',1) + lane['phase'] = int( sigx.get('phase',0 ) * 2 ) + sub_content=[] + sub_content.append( [sigx.get('name',' '), sigx.get('phase',0 ) ] ) + sub_content.append( parseWaveLane( sigx['wave'], int(lane['period'] * lane['hscale'] - 1 ) ) if sigx.get('wave') else None ) + sub_content.append( data_extract(sigx) ) + content.append(sub_content) + + return content + +def findLaneMarkers (lanetext) : + + lcount = 0 + gcount = 0 + ret = [] + for i in range( len( lanetext ) ) : + if lanetext[i] == 'vvv-2' or lanetext[i] == 'vvv-3' or lanetext[i] == 'vvv-4' or lanetext[i] == 'vvv-5' : + lcount += 1 + else : + if lcount !=0 : + ret.append(gcount - ((lcount + 1) / 2)) + lcount = 0 + + gcount += 1 + + if lcount != 0 : + ret.append(gcount - ((lcount + 1) / 2)) + + return ret + +def renderWaveLane (root, content, index) : + + xmax = 0 + xgmax = 0 + glengths = [] + svgns = 'http://www.w3.org/2000/svg' + xlinkns = 'http://www.w3.org/1999/xlink' + xmlns = 'http://www.w3.org/XML/1998/namespace' + for j in range( len(content) ): + name = content[j][0][0] + if name : # check name + g = [ + 'g', + { + 'id': 'wavelane_' + str(j) + '_' + str(index), + 'transform': 'translate(0,' + str(lane['y0'] + j * lane['yo']) + ')' + } + ] + root.append(g) + title = [ + 'text', + { + 'x': lane['tgo'], + 'y': lane['ym'], + 'class': 'info', + 'text-anchor': 'end', + 'xml:space': 'preserve' + }, + ['tspan', name] + ] + g.append(title) + + glengths.append( len(name) * font_width + font_width ) + + xoffset = content[j][0][1] + xoffset = math.ceil(2 * xoffset) - 2 * xoffset if xoffset > 0 else -2 * xoffset + gg = [ + 'g', + { + 'id': 'wavelane_draw_' + str(j) + '_' + str(index), + 'transform': 'translate(' + str( xoffset * lane['xs'] ) + ', 0)' + } + ] + g.append(gg) + + if content[j][1] : + for i in range( len(content[j][1]) ) : + b = [ + 'use', + { + #'id': 'use_' + str(i) + '_' + str(j) + '_' + str(index), + 'xmlns:xlink':xlinkns, + 'xlink:href': '#' + str( content[j][1][i] ), + 'transform': 'translate(' + str(i * lane['xs']) + ')' + } + ] + gg.append(b) + + if content[j][2] and len(content[j][2]) : + labels = findLaneMarkers(content[j][1]) + if len(labels) != 0 : + for k in range( len(labels) ) : + if content[j][2] and k < len(content[j][2]) : + title = [ + 'text', + { + 'x': int(labels[k]) * lane['xs'] + lane['xlabel'], + 'y': lane['ym'], + 'text-anchor': 'middle', + 'xml:space': 'preserve' + }, + ['tspan',content[j][2][k]] + ] + gg.append(title) + + + if len(content[j][1]) > xmax : + xmax = len(content[j][1]) + + lane['xmax'] = xmax + lane['xg'] = xgmax + 20 + return glengths + +def renderMarks (root, content, index) : + + def captext ( g, cxt, anchor, y ) : + + if cxt.get(anchor) and cxt[anchor].get('text') : + tmark = [ + 'text', + { + 'x': float( cxt['xmax'] ) * float( cxt['xs'] ) / 2, + 'y': y, + 'text-anchor': 'middle', + 'fill': '#000', + 'xml:space': 'preserve' + }, cxt[anchor]['text'] + ] + g.append(tmark) + + def ticktock ( g, cxt, ref1, ref2, x, dx, y, length ) : + L = [] + + if cxt.get(ref1) == None or cxt[ref1].get(ref2) == None : + return + + val = cxt[ref1][ref2] + if is_type_str( val ) : + val = val.split() + elif type( val ) is int : + offset = val + val = [] + for i in range ( length ) : + val.append(i + offset) + + if type( val ) is list : + if len( val ) == 0 : + return + elif len( val ) == 1 : + offset = val[0] + if is_type_str(offset) : + L = val + else : + for i in range ( length ) : + L[i] = i + offset + + elif len( val ) == 2: + offset = int(val[0]) + step = int(val[1]) + tmp = val[1].split('.') + if len( tmp ) == 2 : + dp = len( tmp[1] ) + + if is_type_str(offset) or is_type_str(step) : + L = val + else : + offset = step * offset + for i in range( length ) : + L[i] = "{0:.",dp,"f}".format(step * i + offset) + + else : + L = val + + else : + return + + for i in range( length ) : + tmp = L[i] + tmark = [ + 'text', + { + 'x': i * dx + x, + 'y': y, + 'text-anchor': 'middle', + 'class': 'muted', + 'xml:space': 'preserve' + }, str(tmp) + ] + g.append(tmark) + + mstep = 2 * int(lane['hscale']) + mmstep = mstep * lane['xs'] + marks = int( lane['xmax'] / mstep ) + gy = len( content ) * int(lane['yo']) + + g = ['g', {'id': 'gmarks_' + str(index)}] + root.insert(0,g) + + for i in range( marks + 1): + gg = [ + 'path', + { + 'id': 'gmark_' + str(i) + '_' + str(index), + 'd': 'm ' + str(i * mmstep) + ',' + '0' + ' 0,' + str(gy), + 'style': 'stroke:#888;stroke-width:0.5;stroke-dasharray:1,3' + } + ] + g.append( gg ) + + captext(g, lane, 'head', -33 if lane['yh0'] else -13 ) + captext(g, lane, 'foot', gy + ( 45 if lane['yf0'] else 25 ) ) + + ticktock( g, lane, 'head', 'tick', 0, mmstep, -5, marks + 1) + ticktock( g, lane, 'head', 'tock', mmstep / 2, mmstep, -5, marks) + ticktock( g, lane, 'foot', 'tick', 0, mmstep, gy + 15, marks + 1) + ticktock( g, lane, 'foot', 'tock', mmstep / 2, mmstep, gy + 15, marks) + +def renderArcs (root, source, index, top) : + + Stack = [] + Edge = {'words': [], 'frm': 0, 'shape': '', 'to': 0, 'label': ''} + Events = {} + svgns = 'http://www.w3.org/2000/svg' + xmlns = 'http://www.w3.org/XML/1998/namespace' + + if source : + for i in range (len (source) ) : + lane['period'] = source[i].get('period',1) + lane['phase'] = int( source[i].get('phase',0 ) * 2 ) + text = source[i].get('node') + if text: + Stack = text + pos = 0 + while len( Stack ) : + eventname = Stack[0] + Stack=Stack[1:] + if eventname != '.' : + Events[eventname] = { + 'x' : str( int( float( lane['xs'] ) * (2 * pos * lane['period'] * lane['hscale'] - lane['phase'] ) + float( lane['xlabel'] ) ) ), + 'y' : str( int( i * lane['yo'] + lane['y0'] + float( lane['ys'] ) * 0.5 ) ) + } + pos += 1 + + gg = [ 'g', { 'id' : 'wavearcs_' + str( index ) } ] + root.append(gg) + + if top.get('edge') : + for i in range( len ( top['edge'] ) ) : + Edge['words'] = top['edge'][i].split() + Edge['label'] = top['edge'][i][len(Edge['words'][0]):] + Edge['label'] = Edge['label'][1:] + Edge['frm'] = Edge['words'][0][0] + Edge['to'] = Edge['words'][0][-1] + Edge['shape'] = Edge['words'][0][1:-1] + frm = Events[Edge['frm']] + to = Events[Edge['to']] + gmark = [ + 'path', + { + 'id': 'gmark_' + Edge['frm'] + '_' + Edge['to'], + 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + to['x'] + ',' + to['y'], + 'style': 'fill:none;stroke:#00F;stroke-width:1' + } + ] + gg.append(gmark) + dx = float( to['x'] ) - float( frm['x'] ) + dy = float( to['y'] ) - float( frm['y'] ) + lx = (float(frm['x']) + float(to['x'])) / 2 + ly = (float(frm['y']) + float(to['y'])) / 2 + pattern = { + '~' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) }, + '-~' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) }, + '~-' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + '0' + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) }, + '-|' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)}, + '|-' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' 0,' + str(dy) + ' ' + str(dx) + ',0'}, + '-|-' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'}, + '->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none'}, + '~>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)}, + '-~>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)}, + '~->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + '0' + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)}, + '-|>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)}, + '|->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' 0,' + str(dy) + ' ' + str(dx) + ',0'}, + '-|->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'}, + '<->' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none'}, + '<~>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)}, + '<-~>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)}, + '<-|>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)}, + '<-|->': {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'} + } + gmark[1].update( pattern.get( Edge['shape'], { 'style': 'fill:none;stroke:#00F;stroke-width:1' } ) ) + + if Edge['label']: + if Edge['shape'] == '-~' : + lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75 + if Edge['shape'] == '~-' : + lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.25 + if Edge['shape'] == '-|' : + lx = float(to['x']) + if Edge['shape'] == '|-' : + lx = float(frm['x']) + if Edge['shape'] == '-~>': + lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75 + if Edge['shape'] == '~->': + lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.25 + if Edge['shape'] == '-|>' : + lx = float(to['x']) + if Edge['shape'] == '|->' : + lx = float(frm['x']) + if Edge['shape'] == '<-~>': + lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75 + if Edge['shape'] =='<-|>' : + lx = float(to['x']) + + lwidth = len( Edge['label'] ) * font_width + label = [ + 'text', + { + 'style': 'font-size:10px;', + 'text-anchor': 'middle', + 'xml:space': 'preserve', + 'x': int( lx ), + 'y': int( ly + 3 ) + }, + [ 'tspan', Edge['label'] ] + ] + underlabel = [ + 'rect', + { + 'height': 9, + 'style': 'fill:#FFF;', + 'width': lwidth, + 'x': int( lx - lwidth / 2 ), + 'y': int( ly - 5 ) + } + ] + gg.append(underlabel) + gg.append(label) + + for k in Events: + if k.islower() : + if int( Events[k]['x'] ) > 0 : + lwidth = len( k ) * font_width + underlabel = [ + 'rect', + { + 'x': float( Events[k]['x'] ) - float(lwidth) / 2, + 'y': int( Events[k]['y'] ) - 4, + 'height': 8, + 'width': lwidth, + 'style': 'fill:#FFF;' + } + ] + gg.append(underlabel) + label = [ + 'text', + { + 'style': 'font-size:8px;', + 'x': int( Events[k]['x'] ), + 'y': int( Events[k]['y'] ) + 2, + 'width': lwidth, + 'text-anchor': 'middle' + }, + k + ] + gg.append(label) + +def parseConfig (source) : + + lane['hscale'] = 1 + if lane.get('hscale0') : + lane['hscale'] = lane['hscale0'] + + if source and source.get('config') and source.get('config').get('hscale'): + hscale = round(source.get('config').get('hscale')) + if hscale > 0 : + if hscale > 100 : hscale = 100 + lane['hscale'] = hscale + + lane['yh0'] = 0 + lane['yh1'] = 0 + if source and source.get('head') : + lane['head'] = source['head'] + if source.get('head').get('tick',0) == 0 : lane['yh0'] = 20 + if source.get('head').get('tock',0) == 0 : lane['yh0'] = 20 + if source.get('head').get('text') : lane['yh1'] = 46; lane['head']['text'] = source['head']['text'] + + lane['yf0'] = 0 + lane['yf1'] = 0 + if source and source.get('foot') : + lane['foot'] = source['foot'] + if source.get('foot').get('tick',0) == 0 : lane['yf0'] = 20 + if source.get('foot').get('tock',0) == 0 : lane['yf0'] = 20 + if source.get('foot').get('text') : lane['yf1'] = 46; lane['foot']['text'] = source['foot']['text'] + +def rec (tmp, state) : + + name = str( tmp[0] ) + delta_x = 25 + + state['x'] += delta_x + for i in range( len( tmp ) ) : + if type( tmp[i] ) is list : + old_y = state['y'] + rec( tmp[i], state ) + state['groups'].append( {'x':state['xx'], 'y':old_y, 'height':state['y'] - old_y, 'name': state['name'] } ) + elif type( tmp[i] ) is dict : + state['lanes'].append(tmp[i]) + state['width'].append(state['x']) + state['y'] += 1 + + state['xx'] = state['x'] + state['x'] -= delta_x + state['name'] = name + +def insertSVGTemplate (index, parent, source) : + + e = waveskin.WaveSkin['default'] + + if source.get('config') and source.get('config').get('skin') : + if waveskin.WaveSkin.get( source.get('config').get('skin') ) : + e = waveskin.WaveSkin[ source.get('config').get('skin') ] + + if index == 0 : + lane['xs'] = int( e[3][1][2][1]['width'] ) + lane['ys'] = int( e[3][1][2][1]['height'] ) + lane['xlabel'] = int( e[3][1][2][1]['x'] ) + lane['ym'] = int( e[3][1][2][1]['y'] ) + + else : + e = ['svg', {'id': 'svg', 'xmlns': 'http://www.w3.org/2000/svg', 'xmlns:xlink': 'http://www.w3.org/1999/xlink', 'height': '0'}, + ['g', {'id': 'waves'}, + ['g', {'id': 'lanes'}], + ['g', {'id': 'groups'}] + ] + ] + + e[-1][1]['id'] = 'waves_' + str(index) + e[-1][2][1]['id'] = 'lanes_' + str(index) + e[-1][3][1]['id'] = 'groups_' + str(index) + e[1]['id'] = 'svgcontent_' + str(index) + e[1]['height'] = 0 + + parent.extend(e) + +def renderWaveForm (index, source, output) : + + xmax = 0 + root = [] + groups = [] + + if source.get('signal'): + insertSVGTemplate(index, output, source) + parseConfig( source ) + ret = {'x':0, 'y':0, 'xmax':0, 'width':[], 'lanes':[], 'groups':[] } + rec( source['signal'], ret ) + content = parseWaveLanes(ret['lanes']) + glengths = renderWaveLane(root, content, index) + for i in range( len( glengths ) ): + xmax = max( xmax, ( glengths[i] + ret['width'][i] ) ) + renderMarks(root, content, index) + renderArcs(root, ret['lanes'], index, source) + renderGaps(root, ret['lanes'], index) + renderGroups(groups, ret['groups'], index) + lane['xg'] = int( math.ceil( float( xmax - lane['tgo'] ) / float(lane['xs'] ) ) ) * lane['xs'] + width = (lane['xg'] + lane['xs'] * (lane['xmax'] + 1) ) + height = len(content) * lane['yo'] + lane['yh0'] + lane['yh1'] + lane['yf0'] + lane['yf1'] + output[1]={ + 'id' :'svgcontent_' + str(index), + 'xmlns' :"http://www.w3.org/2000/svg", + 'xmlns:xlink':"http://www.w3.org/1999/xlink", + 'width' :str(width), + 'height' :str(height), + 'viewBox' :'0 0 ' + str(width) + ' ' + str(height), + 'overflow' :"hidden" + } + output[-1][2][1]['transform']='translate(' + str(lane['xg'] + 0.5) + ', ' + str((float(lane['yh0']) + float(lane['yh1'])) + 0.5) + ')' + + output[-1][2].extend(root) + output[-1][3].extend(groups) + +def renderGroups (root, groups, index) : + + svgns = 'http://www.w3.org/2000/svg', + xmlns = 'http://www.w3.org/XML/1998/namespace' + + for i in range( len( groups ) ) : + group = [ + 'path', + { + 'id': 'group_' + str(i) + '_' + str(index), + 'd': 'm ' + str( groups[i]['x'] + 0.5 ) + ',' + str( groups[i]['y']* lane['yo'] + 3.5 + lane['yh0'] + lane['yh1'] ) + ' c -3,0 -5,2 -5,5 l 0,' + str( int( groups[i]['height'] * lane['yo'] - 16 ) ) + ' c 0,3 2,5 5,5', + 'style': 'stroke:#0041c4;stroke-width:1;fill:none' + } + ] + root.append(group) + + name = groups[i]['name'] + x = str( int( groups[i]['x'] - 10 ) ) + y = str( int( lane['yo'] * (groups[i]['y'] + (float(groups[i]['height']) / 2)) + lane['yh0'] + lane['yh1'] ) ) + label = [ + ['g', + {'transform': 'translate(' + x + ',' + y + ')'}, + ['g', {'transform': 'rotate(270)'}, + 'text', + { + 'text-anchor': 'middle', + 'class': 'info', + 'xml:space' : 'preserve' + }, + ['tspan',name] + ] + ] + ] + root.append(label) + +def renderGaps (root, source, index) : + + Stack = [] + svgns = 'http://www.w3.org/2000/svg', + xlinkns = 'http://www.w3.org/1999/xlink' + + if source: + + gg = [ + 'g', + { 'id': 'wavegaps_' + str(index) } + ] + + for i in range( len( source )): + lane['period'] = source[i].get('period',1) + lane['phase'] = int( source[i].get('phase',0 ) * 2 ) + + g = [ + 'g', + { + 'id': 'wavegap_' + str(i) + '_' + str(index), + 'transform': 'translate(0,' + str(lane['y0'] + i * lane['yo']) + ')' + } + ] + gg.append(g) + + if source[i].get('wave'): + text = source[i]['wave'] + Stack = text + pos = 0 + while len( Stack ) : + c = Stack [0] + Stack = Stack[1:] + if c == '|' : + b = [ + 'use', + { + 'xmlns:xlink':xlinkns, + 'xlink:href':'#gap', + 'transform': 'translate(' + str(int(float(lane['xs']) * ((2 * pos + 1) * float(lane['period']) * float(lane['hscale']) - float(lane['phase'])))) + ')' + } + ] + g.append(b) + pos += 1 + + root.append( gg ) + +def is_type_str( var ) : + if sys.version_info[0] < 3: + return type( var ) is str or type( var ) is unicode + else: + return type( var ) is str + +def convert_to_svg( root ) : + + svg_output = '' + + if type( root ) is list: + if len(root) >= 2 and type( root[1] ) is dict: + if len( root ) == 2 : + svg_output += '<' + root[0] + convert_to_svg( root[1] ) + '/>\n' + elif len( root ) >= 3 : + svg_output += '<' + root[0] + convert_to_svg( root[1] ) + '>\n' + if len( root ) == 3: + svg_output += convert_to_svg( root[2] ) + else: + svg_output += convert_to_svg( root[2:] ) + svg_output += '\n' + elif type( root[0] ) is list: + for eleml in root: + svg_output += convert_to_svg( eleml ) + else: + svg_output += '<' + root[0] + '>\n' + for eleml in root[1:]: + svg_output += convert_to_svg( eleml ) + svg_output += '\n' + elif type( root ) is dict: + for elemd in root : + svg_output += ' ' + elemd + '="' + str(root[elemd]) + '"' + else: + svg_output += root + + return svg_output + +if __name__ == '__main__': + + if len( sys.argv ) != 5: + print ( 'Usage : ' + sys.argv[0] + ' source svg ' ) + exit(1) + + if sys.argv[3] != 'svg' : + print ( 'Error: only SVG format supported.' ) + exit(1) + + output=[] + inputfile = sys.argv[2] + outputfile = sys.argv[4] + + with open(inputfile,'r') as f: + jinput = json.load(f) + + renderWaveForm(0,jinput,output) + svg_output = convert_to_svg(output) + + with open(outputfile,'w') as f: + f.write( svg_output ) diff --git a/compiler/drc/design_rules.py b/compiler/drc/design_rules.py new file mode 100644 index 00000000..4bc2eaa6 --- /dev/null +++ b/compiler/drc/design_rules.py @@ -0,0 +1,40 @@ +import debug +from drc_value import * +from drc_lut import * + +class design_rules(): + """ + This is a class that implements the design rules structures. + """ + def __init__(self, name): + self.tech_name = name + self.rules = {} + + def add(self, name, value): + self.rules[name] = value + + def __call__(self, name, *args): + rule = self.rules[name] + if callable(rule): + return rule(*args) + else: + return rule + + def __setitem__(self, b, c): + """ + For backward compatibility with existing rules. + """ + self.rules[b] = c + + def __getitem__(self, b): + """ + For backward compatibility with existing rules. + """ + rule = self.rules[b] + if not callable(rule): + return rule + else: + debug.error("Must call complex DRC rule {} with arguments.".format(b),-1) + + + diff --git a/compiler/drc/drc_lut.py b/compiler/drc/drc_lut.py new file mode 100644 index 00000000..07e482c8 --- /dev/null +++ b/compiler/drc/drc_lut.py @@ -0,0 +1,43 @@ +import debug + +class drc_lut(): + """ + Implement a lookup table of rules. + Each element is a tuple with the last value being the rule. + It searches through backwards until all of the key values are + met and returns the rule value. + For exampe, the key values can be width and length, + and it would return the rule for a wire of at least a given width and length. + A dimension can be ignored by passing inf. + """ + def __init__(self, table): + self.table = table + + def __call__(self, *key): + """ + Lookup a given tuple in the table. + """ + if len(key)==0: + first_key = list(sorted(self.table.keys()))[0] + return self.table[first_key] + + for table_key in sorted(self.table.keys(), reverse=True): + if self.match(key, table_key): + return self.table[table_key] + + + def match(self, key1, key2): + """ + Determine if key1>=key2 for all tuple pairs. + (i.e. return false if key10): #does this structure reference any others? for sref in self.structures[name].srefs: #go through each reference if sref.sName in structureNames: #and compare to our list structureNames.remove(sref.sName) + debug.check(len(structureNames)==1,"Multiple possible root structures in the layout: {}".format(str(structureNames))) self.rootStructureName = structureNames[0] def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None, transformPath = [], rotateAngle = 0, transFlags = [0,0,0], coordinates = (0,0)): #since this is a recursive function, must deal with the default - #parameters explicitly + #parameters explicitly if startingStructureName == None: startingStructureName = self.rootStructureName - + #set up the rotation matrix if(rotateAngle == None or rotateAngle == ""): angle = 0 @@ -206,7 +211,11 @@ class VlsiLayout: def initialize(self): self.deduceHierarchy() #self.traverseTheHierarchy() - self.populateCoordinateMap() + self.populateCoordinateMap() + + for layerNumber in self.layerNumbersInUse: + self.processLabelPins(layerNumber) + def populateCoordinateMap(self): def addToXyTree(startingStructureName = None,transformPath = None): @@ -295,6 +304,7 @@ class VlsiLayout: debug.info(1,"DEBUG: Structure %s Found"%StructureName) StructureFound = True + debug.check(StructureFound,"Could not find layout to instantiate {}".format(StructureName)) # If layoutToAdd is a unique object (not this), then copy hierarchy, @@ -478,6 +488,10 @@ class VlsiLayout: return False #these shapes are ok def isPointInsideOfBox(self,pointCoordinates,boxCoordinates): + """ + Check if a point is contained in the shape + """ + debug.check(len(boxCoordinates)==4,"Invalid number of coordinates for box.") leftBound = boxCoordinates[0][0] rightBound = boxCoordinates[0][0] topBound = boxCoordinates[0][1] @@ -499,7 +513,9 @@ class VlsiLayout: return True def isShapeInsideOfBox(self,shapeCoordinates, boxCoordinates): - #go through every point in the shape to test if they are all inside the box + """ + Go through every point in the shape to test if they are all inside the box. + """ for point in shapeCoordinates: if not self.isPointInsideOfBox(point,boxCoordinates): return False @@ -619,7 +635,8 @@ class VlsiLayout: def updateBoundary(self,thisBoundary,cellBoundary): [left_bott_X,left_bott_Y,right_top_X,right_top_Y]=thisBoundary - if cellBoundary==[None,None,None,None]: + # If any are None + if not (cellBoundary[0] and cellBoundary[1] and cellBoundary[2] and cellBoundary[3]): cellBoundary=thisBoundary else: if cellBoundary[0]>left_bott_X: @@ -633,160 +650,89 @@ class VlsiLayout: return cellBoundary - def getLabelDBInfo(self,label_name): + def getTexts(self, layer): """ - Return the coordinates in DB units and layer of all matching labels + Get all of the labels on a given layer only at the root level. """ - label_list = [] - label_layer = None - label_coordinate = [None, None] - - # Why must this be the last one found? It breaks if we return the first. + text_list = [] for Text in self.structures[self.rootStructureName].texts: - if Text.textString == label_name or Text.textString == label_name+"\x00": - label_layer = Text.drawingLayer - label_coordinate = Text.coordinates[0] - if label_layer!=None: - label_list.append((label_coordinate,label_layer)) - - debug.check(len(label_list)>0,"Did not find labels {0}.".format(label_name)) - return label_list - - - def getLabelInfo(self,label_name): - """ - Return the coordinates in USER units and layer of a label - """ - label_list=self.getLabelDBInfo(label_name) - new_list=[] - for label in label_list: - (label_coordinate,label_layer)=label - user_coordinates = [x*self.units[0] for x in label_coordinate] - new_list.append(user_coordinates,label_layer) - return new_list + if Text.drawingLayer == layer: + text_list.append(Text) + return text_list - def getPinShapeByLocLayer(self, coordinate, layer): - """ - Return the largest enclosing rectangle on a layer and at a location. - Coordinates should be in USER units. - """ - db_coordinate = [x/self.units[0] for x in coordinate] - return self.getPinShapeByDBLocLayer(db_coordinate, layer) - - def getPinShapeByDBLocLayer(self, coordinate, layer): - """ - Return the largest enclosing rectangle on a layer and at a location. - Coordinates should be in DB units. - """ - pin_boundaries=self.getAllPinShapesInStructureList(coordinate, layer) - - if len(pin_boundaries) == 0: - debug.warning("Did not find pin on layer {0} at coordinate {1}".format(layer, coordinate)) - - # sort the boundaries, return the max area pin boundary - pin_boundaries.sort(key=boundaryArea,reverse=True) - pin_boundary=pin_boundaries[0] - - # Convert to USER units - pin_boundary=[pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0], - pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]] - - # Make a name if we don't have the pin name - return ["p"+str(coordinate)+"_"+str(layer), layer, pin_boundary] - - def getAllPinShapesByLocLayer(self, coordinate, layer): - """ - Return ALL the enclosing rectangles on the same layer - at the given coordinate. Coordinates should be in USER units. - """ - db_coordinate = [int(x/self.units[0]) for x in coordinate] - return self.getAllPinShapesByDBLocLayer(db_coordinate, layer) - - def getAllPinShapesByDBLocLayer(self, coordinate, layer): - """ - Return ALL the enclosing rectangles on the same layer - at the given coordinate. Input coordinates should be in DB units. - Returns user unit shapes. - """ - pin_boundaries=self.getAllPinShapesInStructureList(coordinate, layer) - - # Convert to user units - new_boundaries = [] - for pin_boundary in pin_boundaries: - new_boundaries.append([pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0], - pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]]) - - return new_boundaries - - def getPinShapeByLabel(self,label_name): + def getPinShape(self, pin_name): """ Search for a pin label and return the largest enclosing rectangle on the same layer as the pin label. + If there are multiple pin lists, return the max of each. """ - label_list=self.getLabelDBInfo(label_name) - shape_list=[] - for label in label_list: - (label_coordinate,label_layer)=label - shape = self.getPinShapeByDBLocLayer(label_coordinate, label_layer) - shape_list.append(shape) - return shape_list + pin_map = self.pins[pin_name] + max_pins = [] + for pin_list in pin_map: + max_pin = None + max_area = 0 + for pin in pin_list: + (layer,boundary) = pin + new_area = boundaryArea(boundary) + if max_pin == None or new_area>max_area: + max_pin = pin + max_area = new_area + max_pins.append(max_pin) - def getAllPinShapesByLabel(self,label_name): + return max_pins + + + def getAllPinShapes(self, pin_name): """ Search for a pin label and return ALL the enclosing rectangles on the same layer as the pin label. """ - - label_list=self.getLabelDBInfo(label_name) - shape_list=[] - for label in label_list: - (label_coordinate,label_layer)=label - shape_list.extend(self.getAllPinShapesByDBLocLayer(label_coordinate, label_layer)) + shape_list = [] + pin_map = self.pins[pin_name] + for pin_list in pin_map: + for pin in pin_list: + (pin_layer, boundary) = pin + shape_list.append(pin) + return shape_list - - def getAllPinShapesInStructureList(self,coordinates,layer): + + + def processLabelPins(self, layer): """ - Given a coordinate, search for enclosing structures on the given layer. - Return all pin shapes. + Find all text labels and create a map to a list of shapes that + they enclose on the given layer. """ - boundaries = [] - for TreeUnit in self.xyTree: - boundaries.extend(self.getPinInStructure(coordinates,layer,TreeUnit)) + # Get the labels on a layer in the root level + labels = self.getTexts(layer) + # Get all of the shapes on the layer at all levels + # and transform them to the current level + shapes = self.getAllShapes(layer) - return boundaries + for label in labels: + label_coordinate = label.coordinates[0] + user_coordinate = [x*self.units[0] for x in label_coordinate] + pin_shapes = [] + for boundary in shapes: + if self.labelInRectangle(user_coordinate,boundary): + pin_shapes.append((layer, boundary)) + label_text = label.textString + # Remove the padding if it exists + if label_text[-1] == "\x00": + label_text = label_text[0:-1] - def getPinInStructure(self,coordinates,layer,structure): - """ - Go through all the shapes in a structure and return the list of shapes - that the label coordinates are inside. + try: + self.pins[label_text] + except KeyError: + self.pins[label_text] = [] + self.pins[label_text].append(pin_shapes) + + + + def getAllShapes(self,layer): """ - - (structureName,structureOrigin,structureuVector,structurevVector)=structure - boundaries = [] - for boundary in self.structures[str(structureName)].boundaries: - # Pin enclosures only work on rectangular pins so ignore any non rectangle - # This may report not finding pins, but the user should fix this by adding a rectangle. - if len(boundary.coordinates)!=5: - continue - if layer==boundary.drawingLayer: - left_bottom=boundary.coordinates[0] - right_top=boundary.coordinates[2] - # Rectangle is [leftx, bottomy, rightx, topy]. - boundaryRect=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]] - boundaryRect=self.transformRectangle(boundaryRect,structureuVector,structurevVector) - boundaryRect=[boundaryRect[0]+structureOrigin[0].item(),boundaryRect[1]+structureOrigin[1].item(), - boundaryRect[2]+structureOrigin[0].item(),boundaryRect[3]+structureOrigin[1].item()] - - if self.labelInRectangle(coordinates,boundaryRect): - boundaries.append(boundaryRect) - - return boundaries - - - def getAllShapesInStructureList(self,layer): - """ - Return all pin shapes on a given layer. + Return all gshapes on a given layer in [llx, lly, urx, ury] format and + user units. """ boundaries = [] for TreeUnit in self.xyTree: @@ -811,7 +757,8 @@ class VlsiLayout: def getShapesInStructure(self,layer,structure): """ - Go through all the shapes in a structure and return the list of shapes. + Go through all the shapes in a structure and return the list of shapes in + the form [llx, lly, urx, ury] """ (structureName,structureOrigin,structureuVector,structurevVector)=structure @@ -819,6 +766,7 @@ class VlsiLayout: boundaries = [] for boundary in self.structures[str(structureName)].boundaries: # FIXME: Right now, this only supports rectangular shapes! + #debug.check(len(boundary.coordinates)==5,"Non-rectangular shape.") if len(boundary.coordinates)!=5: continue if layer==boundary.drawingLayer: @@ -873,8 +821,8 @@ class VlsiLayout: """ Checks if a coordinate is within a given rectangle. Rectangle is [leftx, bottomy, rightx, topy]. """ - coordinate_In_Rectangle_x_range=(coordinate[0]>=int(rectangle[0]))&(coordinate[0]<=int(rectangle[2])) - coordinate_In_Rectangle_y_range=(coordinate[1]>=int(rectangle[1]))&(coordinate[1]<=int(rectangle[3])) + coordinate_In_Rectangle_x_range=(coordinate[0]>=rectangle[0])&(coordinate[0]<=rectangle[2]) + coordinate_In_Rectangle_y_range=(coordinate[1]>=rectangle[1])&(coordinate[1]<=rectangle[3]) if coordinate_In_Rectangle_x_range & coordinate_In_Rectangle_y_range: return True else: diff --git a/compiler/git_id b/compiler/git_id new file mode 100644 index 00000000..e1aa211f --- /dev/null +++ b/compiler/git_id @@ -0,0 +1 @@ +468eb9a4a038201c2b0004fe6e4ae9b2d37fdd57 diff --git a/compiler/globals.py b/compiler/globals.py index af89eaa4..a6360e24 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -24,14 +24,16 @@ def parse_args(): global OPTS option_list = { - optparse.make_option("-b", "--backannotated", action="store_true", dest="run_pex", + optparse.make_option("-b", "--backannotated", action="store_true", dest="use_pex", help="Back annotate simulation"), optparse.make_option("-o", "--output", dest="output_name", help="Base output file name(s) prefix", metavar="FILE"), optparse.make_option("-p", "--outpath", dest="output_path", help="Output file(s) location"), + optparse.make_option("-i", "--inlinecheck", action="store_true", + help="Enable inline LVS/DRC checks", dest="inline_lvsdrc"), optparse.make_option("-n", "--nocheck", action="store_false", - help="Disable inline LVS/DRC checks", dest="check_lvsdrc"), + help="Disable all LVS/DRC checks", dest="check_lvsdrc"), optparse.make_option("-v", "--verbose", action="count", dest="debug_level", help="Increase the verbosity level"), optparse.make_option("-t", "--tech", dest="tech_name", @@ -57,7 +59,7 @@ def parse_args(): # This may be overridden when we read a config file though... if OPTS.tech_name == "": OPTS.tech_name = "scmos" - # Alias SCMOS to AMI 0.5um + # Alias SCMOS to 180nm if OPTS.tech_name == "scmos": OPTS.tech_name = "scn4m_subm" @@ -87,6 +89,7 @@ def print_banner(): print("|=========" + dev_info.center(60) + "=========|") temp_info = "Temp dir: {}".format(OPTS.openram_temp) print("|=========" + temp_info.center(60) + "=========|") + print("|=========" + "See LICENSE for license info".center(60) + "=========|") print("|==============================================================================|") @@ -100,9 +103,16 @@ def check_versions(): minor_required = 5 if not (major_python_version == major_required and minor_python_version >= minor_required): debug.error("Python {0}.{1} or greater is required.".format(major_required,minor_required),-1) - + # FIXME: Check versions of other tools here?? # or, this could be done in each module (e.g. verify, characterizer, etc.) + global OPTS + + try: + import coverage + OPTS.coverage = 1 + except: + OPTS.coverage = 0 def init_openram(config_file, is_unit_test=True): """Initialize the technology, paths, simulators, etc.""" @@ -189,6 +199,7 @@ def read_config(config_file, is_unit_test=True): config_file = re.sub(r'\.py$', "", config_file) # Expand the user if it is used config_file = os.path.expanduser(config_file) + OPTS.config_file = config_file # Add the path to the system path so we can import things in the other directory dir_name = os.path.dirname(config_file) file_name = os.path.basename(config_file) @@ -236,7 +247,7 @@ def read_config(config_file, is_unit_test=True): OPTS.num_words, ports, OPTS.tech_name) - + def end_openram(): @@ -287,7 +298,8 @@ def setup_paths(): # Add all of the subdirs to the python path # These subdirs are modules and don't need to be added: characterizer, verify - for subdir in ["gdsMill", "tests", "modules", "base", "pgates"]: + subdirlist = [ item for item in os.listdir(OPENRAM_HOME) if os.path.isdir(os.path.join(OPENRAM_HOME, item)) ] + for subdir in subdirlist: full_path = "{0}/{1}".format(OPENRAM_HOME,subdir) debug.check(os.path.isdir(full_path), "$OPENRAM_HOME/{0} does not exist: {1}".format(subdir,full_path)) @@ -375,13 +387,17 @@ def import_tech(): OPTS.temperatures = tech.spice["temperatures"] -def print_time(name, now_time, last_time=None): +def print_time(name, now_time, last_time=None, indentation=2): """ Print a statement about the time delta. """ - if last_time: - time = round((now_time-last_time).total_seconds(),1) - else: - time = now_time - print("** {0}: {1} seconds".format(name,time)) + global OPTS + + # Don't print during testing + if not OPTS.is_unit_test or OPTS.debug_level>0: + if last_time: + time = str(round((now_time-last_time).total_seconds(),1)) + " seconds" + else: + time = now_time.strftime('%m/%d/%Y %H:%M:%S') + print("{0} {1}: {2}".format("*"*indentation,name,time)) def report_status(): @@ -398,12 +414,19 @@ def report_status(): debug.error("Tech name must be specified in config file.") print("Technology: {0}".format(OPTS.tech_name)) + print("Total size: {} bits".format(OPTS.word_size*OPTS.num_words*OPTS.num_banks)) print("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size, OPTS.num_words, OPTS.num_banks)) + print("RW ports: {0}\nR-only ports: {1}\nW-only ports: {2}".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports)) if OPTS.netlist_only: print("Netlist only mode (no physical design is being done).") + if not OPTS.inline_lvsdrc: + print("DRC/LVS/PEX is only run on the top-level design.") + if not OPTS.check_lvsdrc: - print("DRC/LVS/PEX checking is disabled.") - + print("DRC/LVS/PEX is completely disabled.") + diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index d6c884a4..a0508b51 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -5,11 +5,11 @@ import design import math from math import log,sqrt,ceil import contact +import pgates from pinv import pinv from pnand2 import pnand2 from pnor2 import pnor2 from vector import vector -from pinvbuf import pinvbuf from globals import OPTS @@ -18,6 +18,7 @@ class bank(design.design): Dynamically generated a single bank including bitcell array, hierarchical_decoder, precharge, (optional column_mux and column decoder), write driver and sense amplifiers. + This can create up to two ports in any combination: rw, w, r. """ def __init__(self, sram_config, name=""): @@ -29,11 +30,6 @@ class bank(design.design): design.design.__init__(self, name) debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words)) - - self.total_write = OPTS.num_rw_ports + OPTS.num_w_ports - self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports - self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports - # The local control signals are gated when we have bank select logic, # so this prefix will be added to all of the input signals to create # the internal gated signals. @@ -44,6 +40,7 @@ class bank(design.design): self.create_netlist() if not OPTS.netlist_only: + debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.") self.create_layout() @@ -51,10 +48,11 @@ class bank(design.design): self.compute_sizes() self.add_pins() self.add_modules() - self.create_modules() + self.create_instances() + def create_layout(self): - self.place_modules() + self.place_instances() self.setup_routing_constraints() self.route_layout() @@ -62,104 +60,282 @@ class bank(design.design): #self.add_lvs_correspondence_points() # Remember the bank center for further placement - self.bank_center=self.offset_all_coordinates().scale(-1,-1) + self.bank_array_ll = self.offset_all_coordinates().scale(-1,-1) + self.bank_array_ur = self.bitcell_array_inst.ur() self.DRC_LVS() - + def add_pins(self): - self.read_index = [] - port_number = 0 - for port in range(OPTS.num_rw_ports): - self.read_index.append("{}".format(port_number)) - port_number += 1 - for port in range(OPTS.num_w_ports): - port_number += 1 - for port in range(OPTS.num_r_ports): - self.read_index.append("{}".format(port_number)) - port_number += 1 - """ Adding pins for Bank module""" - for port in range(self.total_read): + for port in self.read_ports: for bit in range(self.word_size): - self.add_pin("dout{0}[{1}]".format(self.read_index[port],bit),"OUT") - for port in range(self.total_write): + self.add_pin("dout{0}_{1}".format(port,bit),"OUT") + for port in self.write_ports: for bit in range(self.word_size): - self.add_pin("din{0}[{1}]".format(port,bit),"IN") - for port in range(self.total_ports): + self.add_pin("din{0}_{1}".format(port,bit),"IN") + for port in self.all_ports: for bit in range(self.addr_size): - self.add_pin("addr{0}[{1}]".format(port,bit),"INPUT") + self.add_pin("addr{0}_{1}".format(port,bit),"INPUT") # For more than one bank, we have a bank select and name # the signals gated_*. if self.num_banks > 1: - for port in range(self.total_ports): + for port in self.all_ports: self.add_pin("bank_sel{}".format(port),"INPUT") - for port in range(self.total_read): - self.add_pin("s_en{0}".format(self.read_index[port]), "INPUT") - for port in range(self.total_write): + for port in self.read_ports: + self.add_pin("s_en{0}".format(port), "INPUT") + for port in self.read_ports: + self.add_pin("p_en_bar{0}".format(port), "INPUT") + for port in self.write_ports: self.add_pin("w_en{0}".format(port), "INPUT") - for pin in ["clk_buf_bar","clk_buf"]: - self.add_pin(pin,"INPUT") + for port in self.all_ports: + self.add_pin("wl_en{0}".format(port), "INPUT") self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") + def route_layout(self): """ Create routing amoung the modules """ self.route_central_bus() - self.route_precharge_to_bitcell_array() - self.route_col_mux_to_bitcell_array() - self.route_sense_amp_to_col_mux_or_bitcell_array() - self.route_sense_amp_out() - self.route_wordline_driver() - self.route_write_driver() - self.route_row_decoder() - self.route_column_address_lines() - self.route_control_lines() - if self.num_banks > 1: - self.route_bank_select() - self.route_vdd_gnd() + for port in self.all_ports: + self.route_bitlines(port) + self.route_wordline_driver(port) + self.route_row_decoder(port) + self.route_column_address_lines(port) + self.route_control_lines(port) + if self.num_banks > 1: + self.route_bank_select(port) - def create_modules(self): - """ Add modules. The order should not matter! """ + self.route_supplies() - # Above the bitcell array - self.create_bitcell_array() - self.create_precharge_array() + def route_bitlines(self, port): + """ Route the bitlines depending on the port type rw, w, or r. """ - # Below the bitcell array + if port in self.readwrite_ports: + # write_driver -> sense_amp -> (column_mux) -> precharge -> bitcell_array + self.route_write_driver_in(port) + self.route_sense_amp_out(port) + self.route_write_driver_to_sense_amp(port) + self.route_sense_amp_to_column_mux_or_precharge_array(port) + self.route_column_mux_to_precharge_array(port) + self.route_precharge_to_bitcell_array(port) + elif port in self.read_ports: + # sense_amp -> (column_mux) -> precharge -> bitcell_array + self.route_sense_amp_out(port) + self.route_sense_amp_to_column_mux_or_precharge_array(port) + self.route_column_mux_to_precharge_array(port) + self.route_precharge_to_bitcell_array(port) + else: + # write_driver -> (column_mux) -> bitcell_array + self.route_write_driver_in(port) + self.route_write_driver_to_column_mux_or_bitcell_array(port) + self.route_column_mux_to_bitcell_array(port) + + def create_instances(self): + """ Create the instances of the netlist. """ + + self.create_bitcell_array() + + self.create_precharge_array() self.create_column_mux_array() self.create_sense_amp_array() self.create_write_driver_array() - # To the left of the bitcell array self.create_row_decoder() self.create_wordline_driver() self.create_column_decoder() self.create_bank_select() - - def place_modules(self): - """ Add modules. The order should not matter! """ + def compute_instance_offsets(self): + """ + Compute the empty instance offsets for port0 and port1 (if needed) + """ - # Above the bitcell array - self.place_bitcell_array() - self.place_precharge_array() + # These are created even if the port type (e.g. read only) + # doesn't need the instance (e.g. write driver). + + # Create the bottom-up and left to right order of components in each port + # which deepends on the port type rw, w, r + self.vertical_port_order = [] + self.vertical_port_offsets = [] + for port in self.all_ports: + self.vertical_port_order.append([]) + self.vertical_port_offsets.append([None]*4) + + # For later placement, these are fixed in the order: write driver, + # sense amp, clumn mux, precharge, even if the item is not used + # in a given port (it will be None then) + self.vertical_port_order[port].append(self.write_driver_array_inst[port]) + self.vertical_port_order[port].append(self.sense_amp_array_inst[port]) + self.vertical_port_order[port].append(self.column_mux_array_inst[port]) + self.vertical_port_order[port].append(self.precharge_array_inst[port]) + + # For the odd ones they will go on top, so reverse in place + if port%2: + self.vertical_port_order[port]=self.vertical_port_order[port][::-1] + + self.write_driver_offsets = [None]*len(self.all_ports) + self.sense_amp_offsets = [None]*len(self.all_ports) + self.column_mux_offsets = [None]*len(self.all_ports) + self.precharge_offsets = [None]*len(self.all_ports) + + self.wordline_driver_offsets = [None]*len(self.all_ports) + self.row_decoder_offsets = [None]*len(self.all_ports) + + self.column_decoder_offsets = [None]*len(self.all_ports) + self.bank_select_offsets = [None]*len(self.all_ports) + self.compute_instance_port0_offsets() + if len(self.all_ports)==2: + self.compute_instance_port1_offsets() + + + def compute_instance_port0_offsets(self): + """ + Compute the instance offsets for port0. + """ + + port = 0 + + # UPPER RIGHT QUADRANT + # Bitcell array is placed at (0,0) + self.bitcell_array_offset = vector(0,0) + + # LOWER RIGHT QUADRANT # Below the bitcell array - self.place_column_mux_array() - self.place_sense_amp_array() - self.place_write_driver_array() + y_height = 0 + for p in self.vertical_port_order[port]: + if p==None: + continue + y_height += p.height + self.m2_gap + y_offset = -y_height + for i,p in enumerate(self.vertical_port_order[port]): + if p==None: + continue + self.vertical_port_offsets[port][i]=vector(0,y_offset) + y_offset += (p.height + self.m2_gap) + + self.write_driver_offsets[port] = self.vertical_port_offsets[port][0] + self.sense_amp_offsets[port] = self.vertical_port_offsets[port][1] + self.column_mux_offsets[port] = self.vertical_port_offsets[port][2] + self.precharge_offsets[port] = self.vertical_port_offsets[port][3] + + # UPPER LEFT QUADRANT # To the left of the bitcell array - self.place_row_decoder() - self.place_wordline_driver() - self.place_column_decoder() - + # The wordline driver is placed to the right of the main decoder width. + x_offset = self.m2_gap + self.wordline_driver.width + self.wordline_driver_offsets[port] = vector(-x_offset,0) + x_offset += self.row_decoder.width + self.m2_gap + self.row_decoder_offsets[port] = vector(-x_offset,0) - self.place_bank_select() + # LOWER LEFT QUADRANT + # Place the col decoder left aligned with wordline driver plus halfway under row decoder + # Place the col decoder left aligned with row decoder (x_offset doesn't change) + # Below the bitcell array with well spacing + x_offset = self.central_bus_width[port] + self.wordline_driver.width + if self.col_addr_size > 0: + x_offset += self.column_decoder.width + self.col_addr_bus_width + y_offset = self.m2_gap + self.column_decoder.height + else: + y_offset = 0 + y_offset += 2*drc("well_to_well") + self.column_decoder_offsets[port] = vector(-x_offset,-y_offset) + + # Bank select gets placed below the column decoder (x_offset doesn't change) + if self.col_addr_size > 0: + y_offset = min(self.column_decoder_offsets[port].y, self.column_mux_offsets[port].y) + else: + y_offset = self.row_decoder_offsets[port].y + if self.num_banks > 1: + y_offset += self.bank_select.height + drc("well_to_well") + self.bank_select_offsets[port] = vector(-x_offset,-y_offset) + + def compute_instance_port1_offsets(self): + """ + Compute the instance offsets for port1 on the top of the bank. + """ + + port=1 + # The center point for these cells are the upper-right corner of + # the bitcell array. + # The decoder/driver logic is placed on the right and mirrored on Y-axis. + # The write/sense/precharge/mux is placed on the top and mirrored on the X-axis. + + # LOWER LEFT QUADRANT + # Bitcell array is placed at (0,0) + + # UPPER LEFT QUADRANT + # Above the bitcell array + y_offset = self.bitcell_array.height + self.m2_gap + for i,p in enumerate(self.vertical_port_order[port]): + if p==None: + continue + y_offset += (p.height + self.m2_gap) + self.vertical_port_offsets[port][i]=vector(0,y_offset) + + # Reversed order + self.write_driver_offsets[port] = self.vertical_port_offsets[port][3] + self.sense_amp_offsets[port] = self.vertical_port_offsets[port][2] + self.column_mux_offsets[port] = self.vertical_port_offsets[port][1] + self.precharge_offsets[port] = self.vertical_port_offsets[port][0] + + # LOWER RIGHT QUADRANT + # To the left of the bitcell array + # The wordline driver is placed to the right of the main decoder width. + x_offset = self.bitcell_array.width + self.m2_gap + self.wordline_driver.width + self.wordline_driver_offsets[port] = vector(x_offset,0) + x_offset += self.row_decoder.width + self.m2_gap + self.row_decoder_offsets[port] = vector(x_offset,0) + + # UPPER RIGHT QUADRANT + # Place the col decoder right aligned with wordline driver plus halfway under row decoder + # Above the bitcell array with a well spacing + x_offset = self.bitcell_array.width + self.central_bus_width[port] + self.wordline_driver.width + if self.col_addr_size > 0: + x_offset += self.column_decoder.width + self.col_addr_bus_width + y_offset = self.bitcell_array.height + self.column_decoder.height + self.m2_gap + else: + y_offset = self.bitcell_array.height + y_offset += 2*drc("well_to_well") + self.column_decoder_offsets[port] = vector(x_offset,y_offset) + + # Bank select gets placed above the column decoder (x_offset doesn't change) + if self.col_addr_size > 0: + y_offset = max(self.column_decoder_offsets[port].y + self.column_decoder.height, + self.column_mux_offsets[port].y + self.column_mux_array[port].height) + else: + y_offset = self.row_decoder_offsets[port].y + self.bank_select_offsets[port] = vector(x_offset,y_offset) + + def place_instances(self): + """ Place the instances. """ + + self.compute_instance_offsets() + + # UPPER RIGHT QUADRANT + self.place_bitcell_array(self.bitcell_array_offset) + + # LOWER RIGHT QUADRANT + # These are fixed in the order: write driver, sense amp, clumn mux, precharge, + # even if the item is not used in a given port (it will be None then) + self.place_write_driver_array(self.write_driver_offsets) + self.place_sense_amp_array(self.sense_amp_offsets) + self.place_column_mux_array(self.column_mux_offsets) + self.place_precharge_array(self.precharge_offsets) + + # UPPER LEFT QUADRANT + self.place_row_decoder(self.row_decoder_offsets) + self.place_wordline_driver(self.wordline_driver_offsets) + + # LOWER LEFT QUADRANT + self.place_column_decoder(self.column_decoder_offsets) + self.place_bank_select(self.bank_select_offsets) + + def compute_sizes(self): """ Computes the required sizes to create the bank """ @@ -178,33 +354,47 @@ class bank(design.design): # FIXME: This spacing should be width dependent... self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space - # Number of control lines in the bus - self.num_control_lines = 4 # The order of the control signals on the control bus: - self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"] + self.input_control_signals = [] + port_num = 0 + for port in range(OPTS.num_rw_ports): + self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)]) + port_num += 1 + for port in range(OPTS.num_w_ports): + self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num)]) + port_num += 1 + for port in range(OPTS.num_r_ports): + self.input_control_signals.append(["wl_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)]) + port_num += 1 + + # Number of control lines in the bus for each port + self.num_control_lines = [len(x) for x in self.input_control_signals] + + # The width of this bus is needed to place other modules (e.g. decoder) for each port + self.central_bus_width = [self.m2_pitch*x + self.m2_width for x in self.num_control_lines] # These will be outputs of the gaters if this is multibank, if not, normal signals. - if self.num_banks > 1: - self.control_signals = ["gated_"+str for str in self.input_control_signals] - else: - self.control_signals = self.input_control_signals + self.control_signals = [] + for port in self.all_ports: + if self.num_banks > 1: + self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]]) + else: + self.control_signals.append(self.input_control_signals[port]) + # The central bus is the column address (one hot) and row address (binary) if self.col_addr_size>0: self.num_col_addr_lines = 2**self.col_addr_size else: self.num_col_addr_lines = 0 - - # The width of this bus is needed to place other modules (e.g. decoder) - # A width on each side too - self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width + self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines # A space for wells or jogging m2 - self.m2_gap = max(2*drc["pwell_to_nwell"] + drc["well_enclosure_active"], - 2*self.m2_pitch) + self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), + 3*self.m2_pitch) def add_modules(self): - """ Create all the modules using the class loader """ + """ Add all the modules using the class loader """ mod_list = ["bitcell", "decoder", "wordline_driver", "bitcell_array", "sense_amp_array", "precharge_array", @@ -218,37 +408,33 @@ class bank(design.design): setattr (self, "mod_"+mod_name, mod_class) - self.bitcell = self.mod_bitcell() self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols, rows=self.num_rows) self.add_mod(self.bitcell_array) # create arrays of bitline and bitline_bar names for read, write, or all ports - self.read_bl_list = self.bitcell.list_read_bl_names() - self.read_br_list = self.bitcell.list_read_br_names() - - self.write_bl_list = self.bitcell.list_write_bl_names() - self.write_br_list = self.bitcell.list_write_br_names() - - self.total_bl_list = self.bitcell.list_all_bl_names() - self.total_br_list = self.bitcell.list_all_br_names() - - self.total_wl_list = self.bitcell.list_all_wl_names() - self.total_bitline_list = self.bitcell.list_all_bitline_names() + self.bitcell = self.mod_bitcell() + self.bl_names = self.bitcell.list_all_bl_names() + self.br_names = self.bitcell.list_all_br_names() + self.wl_names = self.bitcell.list_all_wl_names() + self.bitline_names = self.bitcell.list_all_bitline_names() self.precharge_array = [] - for port in range(self.total_read): - self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.read_bl_list[port], bitcell_br=self.read_br_list[port])) - self.add_mod(self.precharge_array[port]) + for port in self.all_ports: + if port in self.read_ports: + self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.bl_names[port], bitcell_br=self.br_names[port])) + self.add_mod(self.precharge_array[port]) + else: + self.precharge_array.append(None) if self.col_addr_size > 0: self.column_mux_array = [] - for port in range(self.total_ports): + for port in self.all_ports: self.column_mux_array.append(self.mod_column_mux_array(columns=self.num_cols, word_size=self.word_size, - bitcell_bl=self.read_bl_list[port], - bitcell_br=self.read_br_list[port])) + bitcell_bl=self.bl_names[port], + bitcell_br=self.br_names[port])) self.add_mod(self.column_mux_array[port]) @@ -283,196 +469,224 @@ class bank(design.design): temp = [] for col in range(self.num_cols): - for bitline in self.total_bitline_list: - temp.append(bitline+"[{0}]".format(col)) + for bitline in self.bitline_names: + temp.append(bitline+"_{0}".format(col)) for row in range(self.num_rows): - for wordline in self.total_wl_list: - temp.append(wordline+"[{0}]".format(row)) + for wordline in self.wl_names: + temp.append(wordline+"_{0}".format(row)) temp.append("vdd") temp.append("gnd") self.connect_inst(temp) - def place_bitcell_array(self): + + def place_bitcell_array(self, offset): """ Placing Bitcell Array """ - self.bitcell_array_inst.place(vector(0,0)) + self.bitcell_array_inst.place(offset) def create_precharge_array(self): """ Creating Precharge """ - self.precharge_array_inst = [] - for port in range(self.total_read): - self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(port), - mod=self.precharge_array[port])) + self.precharge_array_inst = [None]*len(self.all_ports) + for port in self.read_ports: + self.precharge_array_inst[port]=self.add_inst(name="precharge_array{}".format(port), + mod=self.precharge_array[port]) temp = [] for i in range(self.num_cols): - temp.append(self.read_bl_list[port]+"[{0}]".format(i)) - temp.append(self.read_br_list[port]+"[{0}]".format(i)) - temp.extend([self.prefix+"clk_buf_bar", "vdd"]) + temp.append(self.bl_names[port]+"_{0}".format(i)) + temp.append(self.br_names[port]+"_{0}".format(i)) + temp.extend([self.prefix+"p_en_bar{0}".format(port), "vdd"]) self.connect_inst(temp) - def place_precharge_array(self): + + def place_precharge_array(self, offsets): """ Placing Precharge """ + + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place precharge array.") + + for port in self.read_ports: + if port%2 == 1: + mirror = "MX" + else: + mirror = "R0" + self.precharge_array_inst[port].place(offset=offsets[port], mirror=mirror) - # FIXME: place for multiport - for port in range(self.total_read): - # The wells must be far enough apart - # The enclosure is for the well and the spacing is to the bitcell wells - y_offset = self.bitcell_array.height + self.m2_gap - self.precharge_array_inst[port].place(vector(0,y_offset)) def create_column_mux_array(self): """ Creating Column Mux when words_per_row > 1 . """ + self.column_mux_array_inst = [None]*len(self.all_ports) + if self.col_addr_size == 0: return - self.col_mux_array_inst = [] - for port in range(self.total_ports): - self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(port), - mod=self.column_mux_array[port])) + for port in self.all_ports: + self.column_mux_array_inst[port] = self.add_inst(name="column_mux_array{}".format(port), + mod=self.column_mux_array[port]) temp = [] for col in range(self.num_cols): - temp.append(self.total_bl_list[port]+"[{0}]".format(col)) - temp.append(self.total_br_list[port]+"[{0}]".format(col)) + temp.append(self.bl_names[port]+"_{0}".format(col)) + temp.append(self.br_names[port]+"_{0}".format(col)) for word in range(self.words_per_row): - temp.append("sel{0}[{1}]".format(port,word)) + temp.append("sel{0}_{1}".format(port,word)) for bit in range(self.word_size): - temp.append(self.total_bl_list[port]+"_out[{0}]".format(bit)) - temp.append(self.total_br_list[port]+"_out[{0}]".format(bit)) + temp.append(self.bl_names[port]+"_out_{0}".format(bit)) + temp.append(self.br_names[port]+"_out_{0}".format(bit)) temp.append("gnd") self.connect_inst(temp) - def place_column_mux_array(self): + + + def place_column_mux_array(self, offsets): """ Placing Column Mux when words_per_row > 1 . """ - if self.col_addr_size > 0: - self.column_mux_height = self.column_mux_array[0].height + self.m2_gap - else: - self.column_mux_height = 0 + if self.col_addr_size == 0: return - for port in range(self.total_ports): - y_offset = self.column_mux_height - self.col_mux_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column mux array.") + + for port in self.all_ports: + if port%2 == 1: + mirror = "MX" + else: + mirror = "R0" + self.column_mux_array_inst[port].place(offset=offsets[port], mirror=mirror) + def create_sense_amp_array(self): """ Creating Sense amp """ - self.sense_amp_array_inst = [] - for port in range(self.total_read): - self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(port), - mod=self.sense_amp_array)) + self.sense_amp_array_inst = [None]*len(self.all_ports) + for port in self.read_ports: + self.sense_amp_array_inst[port] = self.add_inst(name="sense_amp_array{}".format(port), + mod=self.sense_amp_array) temp = [] for bit in range(self.word_size): - temp.append("dout{0}[{1}]".format(self.read_index[port],bit)) + temp.append("dout{0}_{1}".format(port,bit)) if self.words_per_row == 1: - temp.append(self.read_bl_list[port]+"[{0}]".format(bit)) - temp.append(self.read_br_list[port]+"[{0}]".format(bit)) + temp.append(self.bl_names[port]+"_{0}".format(bit)) + temp.append(self.br_names[port]+"_{0}".format(bit)) else: - temp.append(self.read_bl_list[port]+"_out[{0}]".format(bit)) - temp.append(self.read_br_list[port]+"_out[{0}]".format(bit)) + temp.append(self.bl_names[port]+"_out_{0}".format(bit)) + temp.append(self.br_names[port]+"_out_{0}".format(bit)) - temp.extend([self.prefix+"s_en{0}".format(port), "vdd", "gnd"]) + temp.extend([self.prefix+"s_en{}".format(port), "vdd", "gnd"]) self.connect_inst(temp) - def place_sense_amp_array(self): + + def place_sense_amp_array(self, offsets): """ Placing Sense amp """ + + debug.check(len(offsets)>=len(self.read_ports), "Insufficient offsets to place sense amp array.") + for port in self.read_ports: + if port%2 == 1: + mirror = "MX" + else: + mirror = "R0" + self.sense_amp_array_inst[port].place(offset=offsets[port], mirror=mirror) - # FIXME: place for multiport - for port in range(self.total_read): - y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap - self.sense_amp_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) def create_write_driver_array(self): """ Creating Write Driver """ - self.write_driver_array_inst = [] - for port in range(self.total_write): - self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port), - mod=self.write_driver_array)) + self.write_driver_array_inst = [None]*len(self.all_ports) + for port in self.write_ports: + self.write_driver_array_inst[port] = self.add_inst(name="write_driver_array{}".format(port), + mod=self.write_driver_array) temp = [] for bit in range(self.word_size): - temp.append("din{0}[{1}]".format(port,bit)) + temp.append("din{0}_{1}".format(port,bit)) for bit in range(self.word_size): if (self.words_per_row == 1): - temp.append(self.write_bl_list[port]+"[{0}]".format(bit)) - temp.append(self.write_br_list[port]+"[{0}]".format(bit)) + temp.append(self.bl_names[port]+"_{0}".format(bit)) + temp.append(self.br_names[port]+"_{0}".format(bit)) else: - temp.append(self.write_bl_list[port]+"_out[{0}]".format(bit)) - temp.append(self.write_br_list[port]+"_out[{0}]".format(bit)) + temp.append(self.bl_names[port]+"_out_{0}".format(bit)) + temp.append(self.br_names[port]+"_out_{0}".format(bit)) temp.extend([self.prefix+"w_en{0}".format(port), "vdd", "gnd"]) self.connect_inst(temp) - def place_write_driver_array(self): + + def place_write_driver_array(self, offsets): """ Placing Write Driver """ - # FIXME: place for multiport - for port in range(self.total_write): - y_offset = self.sense_amp_array.height + self.column_mux_height \ - + self.m2_gap + self.write_driver_array.height - self.write_driver_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) + debug.check(len(offsets)>=len(self.write_ports), "Insufficient offsets to place write driver array.") + for port in self.write_ports: + if port%2 == 1: + mirror = "MX" + else: + mirror = "R0" + self.write_driver_array_inst[port].place(offset=offsets[port], mirror=mirror) def create_row_decoder(self): """ Create the hierarchical row decoder """ - self.row_decoder_inst = [] - for port in range(self.total_ports): - self.row_decoder_inst.append(self.add_inst(name="row_decoder{}".format(port), - mod=self.row_decoder)) + self.row_decoder_inst = [None]*len(self.all_ports) + for port in self.all_ports: + self.row_decoder_inst[port] = self.add_inst(name="row_decoder{}".format(port), + mod=self.row_decoder) temp = [] for bit in range(self.row_addr_size): - temp.append("addr{0}[{1}]".format(port,bit+self.col_addr_size)) + temp.append("addr{0}_{1}".format(port,bit+self.col_addr_size)) for row in range(self.num_rows): - temp.append("dec_out{0}[{1}]".format(port,row)) + temp.append("dec_out{0}_{1}".format(port,row)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - def place_row_decoder(self): + + def place_row_decoder(self, offsets): """ Place the hierarchical row decoder """ + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.") + # The address and control bus will be in between decoder and the main memory array # This bus will route address bits to the decoder input and column mux inputs. # The wires are actually routed after we placed the stuff on both sides. # The predecoder is below the x-axis and the main decoder is above the x-axis # The address flop and decoder are aligned in the x coord. - # FIXME: place for multiport - for port in range(self.total_ports): - x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) - self.row_decoder_inst[port].place(vector(x_offset,0)) + for port in self.all_ports: + if port%2 == 1: + mirror = "MY" + else: + mirror = "R0" + self.row_decoder_inst[port].place(offset=offsets[port], mirror=mirror) def create_wordline_driver(self): """ Create the Wordline Driver """ - self.wordline_driver_inst = [] - for port in range(self.total_ports): - self.wordline_driver_inst.append(self.add_inst(name="wordline_driver{}".format(port), - mod=self.wordline_driver)) + self.wordline_driver_inst = [None]*len(self.all_ports) + for port in self.all_ports: + self.wordline_driver_inst[port] = self.add_inst(name="wordline_driver{}".format(port), + mod=self.wordline_driver) temp = [] for row in range(self.num_rows): - temp.append("dec_out{0}[{1}]".format(port,row)) + temp.append("dec_out{0}_{1}".format(port,row)) for row in range(self.num_rows): - temp.append(self.total_wl_list[port]+"[{0}]".format(row)) - temp.append(self.prefix+"clk_buf") + temp.append(self.wl_names[port]+"_{0}".format(row)) + temp.append(self.prefix+"wl_en{0}".format(port)) temp.append("vdd") temp.append("gnd") self.connect_inst(temp) - def place_wordline_driver(self): + + def place_wordline_driver(self, offsets): """ Place the Wordline Driver """ - # FIXME: place for multiport - for port in range(self.total_ports): - # The wordline driver is placed to the right of the main decoder width. - x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch - self.wordline_driver_inst[port].place(vector(x_offset,0)) + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place wordline driver array.") + + for port in self.all_ports: + if port%2 == 1: + mirror = "MY" + else: + mirror = "R0" + self.wordline_driver_inst[port].place(offset=offsets[port], mirror=mirror) def create_column_decoder(self): @@ -483,42 +697,49 @@ class bank(design.design): if self.col_addr_size == 0: return elif self.col_addr_size == 1: - self.col_decoder = pinvbuf(height=self.mod_dff.height) - self.add_mod(self.col_decoder) + from pinvbuf import pinvbuf + self.column_decoder = pinvbuf(height=self.mod_dff.height) elif self.col_addr_size == 2: - self.col_decoder = self.row_decoder.pre2_4 + from hierarchical_predecode2x4 import hierarchical_predecode2x4 as pre2x4 + self.column_decoder = pre2x4(height=self.mod_dff.height) elif self.col_addr_size == 3: - self.col_decoder = self.row_decoder.pre3_8 + from hierarchical_predecode3x8 import hierarchical_predecode3x8 as pre3x8 + self.column_decoder = pre3x8(height=self.mod_dff.height) else: # No error checking before? debug.error("Invalid column decoder?",-1) - - self.col_decoder_inst = [] - for port in range(self.total_ports): - self.col_decoder_inst.append(self.add_inst(name="col_address_decoder{}".format(port), - mod=self.col_decoder)) + self.add_mod(self.column_decoder) + + self.column_decoder_inst = [None]*len(self.all_ports) + for port in self.all_ports: + self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port), + mod=self.column_decoder) temp = [] for bit in range(self.col_addr_size): - temp.append("addr{0}[{1}]".format(port,bit)) + temp.append("addr{0}_{1}".format(port,bit)) for bit in range(self.num_col_addr_lines): - temp.append("sel{0}[{1}]".format(port,bit)) + temp.append("sel{0}_{1}".format(port,bit)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - def place_column_decoder(self): + + def place_column_decoder(self, offsets): """ Place a 2:4 or 3:8 column address decoder. """ if self.col_addr_size == 0: return - # FIXME: place for multiport - for port in range(self.total_ports): - # Place the col decoder right aligned with row decoder - x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) - y_off = -(self.col_decoder.height + 2*drc["well_to_well"]) - self.col_decoder_inst[port].place(vector(x_off,y_off)) + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column decoder.") + + for port in self.all_ports: + if port%2 == 1: + mirror = "XY" + else: + mirror = "R0" + self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror) + def create_bank_select(self): @@ -527,92 +748,69 @@ class bank(design.design): if not self.num_banks > 1: return - self.bank_select_inst = [] - for port in range(self.total_ports): - self.bank_select_inst.append(self.add_inst(name="bank_select{}".format(port), - mod=self.bank_select)) + self.bank_select_inst = [None]*len(self.all_ports) + for port in self.all_ports: + self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port), + mod=self.bank_select) temp = [] - temp.extend(self.input_control_signals) + temp.extend(self.input_control_signals[port]) temp.append("bank_sel{}".format(port)) - temp.extend(self.control_signals) + temp.extend(self.control_signals[port]) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - def place_bank_select(self): + + def place_bank_select(self, offsets): """ Place the bank select logic. """ if not self.num_banks > 1: return - - # FIXME: place for multiport - for port in range(self.total_ports): - x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) - if self.col_addr_size > 0: - y_off = min(self.col_decoder_inst[0].by(), self.col_mux_array_inst[0].by()) - else: - y_off = self.row_decoder_inst[0].by() - y_off -= (self.bank_select.height + drc["well_to_well"]) - self.bank_select_pos = vector(x_off,y_off) - self.bank_select_inst[port].place(self.bank_select_pos) + + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place bank select logic.") + + for port in self.all_ports: + self.bank_select_inst[port].place(offsets[port]) - def route_vdd_gnd(self): + def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ + for inst in self.insts: + self.copy_power_pins(inst,"vdd") + self.copy_power_pins(inst,"gnd") - # These are the instances that every bank has - top_instances = [self.bitcell_array_inst] - for port in range(self.total_read): - #top_instances.append(self.precharge_array_inst[port]) - top_instances.append(self.sense_amp_array_inst[port]) - for port in range(self.total_write): - top_instances.append(self.write_driver_array_inst[port]) - for port in range(self.total_ports): - top_instances.extend([self.row_decoder_inst[port], - self.wordline_driver_inst[port]]) - # Add these if we use the part... - if self.col_addr_size > 0: - top_instances.append(self.col_decoder_inst[port]) - #top_instances.append(self.col_mux_array_inst[port]) - - if self.num_banks > 1: - top_instances.append(self.bank_select_inst[port]) - - if self.col_addr_size > 0: - for port in range(self.total_ports): - self.copy_layout_pin(self.col_mux_array_inst[port], "gnd") - for port in range(self.total_read): - self.copy_layout_pin(self.precharge_array_inst[port], "vdd") - - for inst in top_instances: - # Column mux has no vdd - #if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst[0]): - self.copy_layout_pin(inst, "vdd") - # Precharge has no gnd - #if inst != self.precharge_array_inst[port]: - self.copy_layout_pin(inst, "gnd") - - def route_bank_select(self): + def route_bank_select(self, port): """ Route the bank select logic. """ - for port in range(self.total_ports): - for input_name in self.input_control_signals+["bank_sel"]: - self.copy_layout_pin(self.bank_select_inst[port], input_name) - - for gated_name in self.control_signals: - # Connect the inverter output to the central bus - out_pos = self.bank_select_inst[port].get_pin(gated_name).rc() - bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y) - self.add_path("metal3",[out_pos, bus_pos]) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=bus_pos, - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=out_pos, - rotate=90) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=out_pos, - rotate=90) + if self.port_id[port] == "rw": + bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"] + gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"] + elif self.port_id[port] == "w": + bank_sel_signals = ["clk_buf", "w_en", "bank_sel"] + gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en"] + else: + bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"] + gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"] + + copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)] + for signal in range(len(copy_control_signals)): + self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal]) + + for signal in range(len(gated_bank_sel_signals)): + # Connect the inverter output to the central bus + out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc() + name = self.control_signals[port][signal] + bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y) + self.add_path("metal3",[out_pos, bus_pos]) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=bus_pos, + rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=out_pos, + rotate=90) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=out_pos, + rotate=90) def setup_routing_constraints(self): @@ -621,30 +819,11 @@ class bank(design.design): control bus, power ring, etc. """ - #The minimum point is either the bottom of the address flops, - #the column decoder (if there is one). - write_driver_min_y_offset = self.write_driver_array_inst[0].by() - 3*self.m2_pitch - row_decoder_min_y_offset = self.row_decoder_inst[0].by() + self.max_y_offset = max([x.uy() for x in self.insts]) + 3*self.m1_width + self.min_y_offset = min([x.by() for x in self.insts]) - if self.col_addr_size > 0: - col_decoder_min_y_offset = self.col_decoder_inst[0].by() - else: - col_decoder_min_y_offset = row_decoder_min_y_offset - - if self.num_banks>1: - # The control gating logic is below the decoder - # Min of the control gating logic and write driver. - self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, write_driver_min_y_offset) - else: - # Just the min of the decoder logic logic and write driver. - self.min_y_offset = min(col_decoder_min_y_offset, write_driver_min_y_offset) - - # The max point is always the top of the precharge bitlines - # Add a vdd and gnd power rail above the array - # FIXME: Update multiport - self.max_y_offset = self.precharge_array_inst[0].uy() + 3*self.m1_width - self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width - self.min_x_offset = self.row_decoder_inst[0].lx() + self.max_x_offset = max([x.rx() for x in self.insts]) + 3*self.m1_width + self.min_x_offset = min([x.lx() for x in self.insts]) # # Create the core bbox for the power rings ur = vector(self.max_x_offset, self.max_y_offset) @@ -654,194 +833,302 @@ class bank(design.design): self.height = ur.y - ll.y self.width = ur.x - ll.x - def route_central_bus(self): """ Create the address, supply, and control signal central bus lines. """ # Overall central bus width. It includes all the column mux lines, # and control lines. + + self.bus_xoffset = [None]*len(self.all_ports) + # Port 0 # The bank is at (0,0), so this is to the left of the y-axis. # 2 pitches on the right for vias/jogs to access the inputs - control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset) - control_bus_length = self.max_y_offset - self.min_y_offset - self.bus_xoffset = self.create_bus(layer="metal2", - pitch=self.m2_pitch, - offset=control_bus_offset, - names=self.control_signals, - length=control_bus_length, - vertical=True, - make_pins=(self.num_banks==1)) + control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_width, self.min_y_offset) + # The control bus is routed up to two pitches below the bitcell array + control_bus_length = -2*self.m1_pitch - self.min_y_offset + self.bus_xoffset[0] = self.create_bus(layer="metal2", + pitch=self.m2_pitch, + offset=control_bus_offset, + names=self.control_signals[0], + length=control_bus_length, + vertical=True, + make_pins=(self.num_banks==1)) + + # Port 1 + if len(self.all_ports)==2: + # The other control bus is routed up to two pitches above the bitcell array + control_bus_length = self.max_y_offset - self.bitcell_array.height - 2*self.m1_pitch + control_bus_offset = vector(self.bitcell_array.width + self.m2_width, + self.max_y_offset - control_bus_length) + + self.bus_xoffset[1] = self.create_bus(layer="metal2", + pitch=self.m2_pitch, + offset=control_bus_offset, + names=self.control_signals[1], + length=control_bus_length, + vertical=True, + make_pins=(self.num_banks==1)) + - - - def route_precharge_to_bitcell_array(self): + def route_precharge_to_bitcell_array(self, port): """ Routing of BL and BR between pre-charge and bitcell array """ - # FIXME: Update for multiport - for port in range(self.total_read): - for col in range(self.num_cols): - precharge_bl = self.precharge_array_inst[port].get_pin("bl[{}]".format(col)).bc() - precharge_br = self.precharge_array_inst[port].get_pin("br[{}]".format(col)).bc() - bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"[{}]".format(col)).uc() - bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"[{}]".format(col)).uc() + inst2 = self.precharge_array_inst[port] + inst1 = self.bitcell_array_inst + inst1_bl_name = self.bl_names[port]+"_{}" + inst1_br_name = self.br_names[port]+"_{}" + self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols, + inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) + + - yoffset = 0.5*(precharge_bl.y+bitcell_bl.y) - self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset), - vector(bitcell_bl.x,yoffset), bitcell_bl]) - self.add_path("metal2",[precharge_br, vector(precharge_br.x,yoffset), - vector(bitcell_br.x,yoffset), bitcell_br]) - - - def route_col_mux_to_bitcell_array(self): - """ Routing of BL and BR between col mux and bitcell array """ + def route_column_mux_to_precharge_array(self, port): + """ Routing of BL and BR between col mux and precharge array """ # Only do this if we have a column mux! if self.col_addr_size==0: return - # FIXME: Update for multiport - for port in range(self.total_ports): - for col in range(self.num_cols): - col_mux_bl = self.col_mux_array_inst[port].get_pin("bl[{}]".format(col)).uc() - col_mux_br = self.col_mux_array_inst[port].get_pin("br[{}]".format(col)).uc() - bitcell_bl = self.bitcell_array_inst.get_pin(self.total_bl_list[port]+"[{}]".format(col)).bc() - bitcell_br = self.bitcell_array_inst.get_pin(self.total_br_list[port]+"[{}]".format(col)).bc() + inst1 = self.column_mux_array_inst[port] + inst2 = self.precharge_array_inst[port] + self.connect_bitlines(inst1, inst2, self.num_cols) - yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y) - self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset), - vector(bitcell_bl.x,yoffset), bitcell_bl]) - self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset), - vector(bitcell_br.x,yoffset), bitcell_br]) + def route_column_mux_to_bitcell_array(self, port): + """ Routing of BL and BR between col mux bitcell array """ + + # Only do this if we have a column mux! + if self.col_addr_size==0: + return - def route_sense_amp_to_col_mux_or_bitcell_array(self): + inst2 = self.column_mux_array_inst[port] + inst1 = self.bitcell_array_inst + inst1_bl_name = self.bl_names[port]+"_{}" + inst1_br_name = self.br_names[port]+"_{}" + + # The column mux is constructed to match the bitline pitch, so we can directly connect + # here and not channel route the bitlines. + self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols, + inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) + + + + def route_sense_amp_to_column_mux_or_precharge_array(self, port): + """ Routing of BL and BR between sense_amp and column mux or precharge array """ + inst2 = self.sense_amp_array_inst[port] + + if self.col_addr_size>0: + # Sense amp is connected to the col mux + inst1 = self.column_mux_array_inst[port] + inst1_bl_name = "bl_out_{}" + inst1_br_name = "br_out_{}" + else: + # Sense amp is directly connected to the precharge array + inst1 = self.precharge_array_inst[port] + inst1_bl_name = "bl_{}" + inst1_br_name = "br_{}" + + self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, + inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) + + def route_write_driver_to_column_mux_or_bitcell_array(self, port): """ Routing of BL and BR between sense_amp and column mux or bitcell array """ + inst2 = self.write_driver_array_inst[port] + + if self.col_addr_size>0: + # Write driver is connected to the col mux + inst1 = self.column_mux_array_inst[port] + inst1_bl_name = "bl_out_{}" + inst1_br_name = "br_out_{}" + else: + # Write driver is directly connected to the bitcell array + inst1 = self.bitcell_array_inst + inst1_bl_name = self.bl_names[port]+"_{}" + inst1_br_name = self.br_names[port]+"_{}" + + self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, + inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) + + def route_write_driver_to_sense_amp(self, port): + """ Routing of BL and BR between write driver and sense amp """ + + inst1 = self.write_driver_array_inst[port] + inst2 = self.sense_amp_array_inst[port] - for port in range(self.total_read): - for bit in range(self.word_size): - sense_amp_bl = self.sense_amp_array_inst[port].get_pin("bl[{}]".format(bit)).uc() - sense_amp_br = self.sense_amp_array_inst[port].get_pin("br[{}]".format(bit)).uc() + # These should be pitch matched in the cell library, + # but just in case, do a channel route. + self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size) - if self.col_addr_size>0: - # Sense amp is connected to the col mux - connect_bl = self.col_mux_array_inst[port].get_pin("bl_out[{}]".format(bit)).bc() - connect_br = self.col_mux_array_inst[port].get_pin("br_out[{}]".format(bit)).bc() - else: - # Sense amp is directly connected to the bitcell array - connect_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"[{}]".format(bit)).bc() - connect_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"[{}]".format(bit)).bc() - - yoffset = 0.5*(sense_amp_bl.y+connect_bl.y) - self.add_path("metal2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset), - vector(connect_bl.x,yoffset), connect_bl]) - self.add_path("metal2",[sense_amp_br, vector(sense_amp_br.x,yoffset), - vector(connect_br.x,yoffset), connect_br]) - - - def route_sense_amp_out(self): - """ Add pins for the sense amp output """ - # FIXME: Update for multiport + def route_sense_amp_out(self, port): + """ Add pins for the sense amp output """ + for bit in range(self.word_size): - data_pin = self.sense_amp_array_inst[0].get_pin("data[{}]".format(bit)) - self.add_layout_pin_rect_center(text="dout0[{}]".format(bit), + data_pin = self.sense_amp_array_inst[port].get_pin("data_{}".format(bit)) + self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port,bit), layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), width=data_pin.width()) - - def route_row_decoder(self): + + def route_row_decoder(self, port): """ Routes the row decoder inputs and supplies """ - # FIXME: Update for multiport # Create inputs for the row address lines for row in range(self.row_addr_size): addr_idx = row + self.col_addr_size - decoder_name = "addr[{}]".format(row) - addr_name = "addr0[{}]".format(addr_idx) - self.copy_layout_pin(self.row_decoder_inst[0], decoder_name, addr_name) + decoder_name = "addr_{}".format(row) + addr_name = "addr{0}_{1}".format(port,addr_idx) + self.copy_layout_pin(self.row_decoder_inst[port], decoder_name, addr_name) - def route_write_driver(self): + def route_write_driver_in(self, port): """ Connecting write driver """ - - for row in range(self.word_size): - data_name = "data[{}]".format(row) - din_name = "din0[{}]".format(row) - self.copy_layout_pin(self.write_driver_array_inst[0], data_name, din_name) - - - def route_wordline_driver(self): + for row in range(self.word_size): + data_name = "data_{}".format(row) + din_name = "din{0}_{1}".format(port,row) + self.copy_layout_pin(self.write_driver_array_inst[port], data_name, din_name) + + def channel_route_bitlines(self, inst1, inst2, num_bits, + inst1_bl_name="bl_{}", inst1_br_name="br_{}", + inst2_bl_name="bl_{}", inst2_br_name="br_{}"): + """ + Route the bl and br of two modules using the channel router. + """ + + # determine top and bottom automatically. + # since they don't overlap, we can just check the bottom y coordinate. + if inst1.by() < inst2.by(): + (bottom_inst, bottom_bl_name, bottom_br_name) = (inst1, inst1_bl_name, inst1_br_name) + (top_inst, top_bl_name, top_br_name) = (inst2, inst2_bl_name, inst2_br_name) + else: + (bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name) + (top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name) + + + # Channel route each mux separately since we don't minimize the number + # of tracks in teh channel router yet. If we did, we could route all the bits at once! + offset = bottom_inst.ul() + vector(0,self.m1_pitch) + for bit in range(num_bits): + bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))] + top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))] + route_map = list(zip(bottom_names, top_names)) + self.create_horizontal_channel_route(route_map, offset) + + + def connect_bitlines(self, inst1, inst2, num_bits, + inst1_bl_name="bl_{}", inst1_br_name="br_{}", + inst2_bl_name="bl_{}", inst2_br_name="br_{}"): + """ + Connect the bl and br of two modules. + This assumes that they have sufficient space to create a jog + in the middle between the two modules (if needed). + """ + + # determine top and bottom automatically. + # since they don't overlap, we can just check the bottom y coordinate. + if inst1.by() < inst2.by(): + (bottom_inst, bottom_bl_name, bottom_br_name) = (inst1, inst1_bl_name, inst1_br_name) + (top_inst, top_bl_name, top_br_name) = (inst2, inst2_bl_name, inst2_br_name) + else: + (bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name) + (top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name) + + for col in range(num_bits): + bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col)).uc() + bottom_br = bottom_inst.get_pin(bottom_br_name.format(col)).uc() + top_bl = top_inst.get_pin(top_bl_name.format(col)).bc() + top_br = top_inst.get_pin(top_br_name.format(col)).bc() + + yoffset = 0.5*(top_bl.y+bottom_bl.y) + self.add_path("metal2",[bottom_bl, vector(bottom_bl.x,yoffset), + vector(top_bl.x,yoffset), top_bl]) + self.add_path("metal2",[bottom_br, vector(bottom_br.x,yoffset), + vector(top_br.x,yoffset), top_br]) + + + def route_wordline_driver(self, port): + """ Connect Wordline driver to bitcell array wordline """ + if port%2: + self.route_wordline_driver_right(port) + else: + self.route_wordline_driver_left(port) + + def route_wordline_driver_left(self, port): """ Connecting Wordline driver output to Bitcell WL connection """ for row in range(self.num_rows): # The pre/post is to access the pin from "outside" the cell to avoid DRCs - decoder_out_pos = self.row_decoder_inst[0].get_pin("decode[{}]".format(row)).rc() - driver_in_pos = self.wordline_driver_inst[0].get_pin("in[{}]".format(row)).lc() + decoder_out_pos = self.row_decoder_inst[port].get_pin("decode_{}".format(row)).rc() + driver_in_pos = self.wordline_driver_inst[port].get_pin("in_{}".format(row)).lc() mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos]) # The mid guarantees we exit the input cell to the right. - driver_wl_pos = self.wordline_driver_inst[0].get_pin("wl[{}]".format(row)).rc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[0]+"[{}]".format(row)).lc() + driver_wl_pos = self.wordline_driver_inst[port].get_pin("wl_{}".format(row)).rc() + bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).lc() mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0) mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1) self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) - - def route_column_address_lines(self): + def route_wordline_driver_right(self, port): + """ Connecting Wordline driver output to Bitcell WL connection """ + + for row in range(self.num_rows): + # The pre/post is to access the pin from "outside" the cell to avoid DRCs + decoder_out_pos = self.row_decoder_inst[port].get_pin("decode_{}".format(row)).lc() + driver_in_pos = self.wordline_driver_inst[port].get_pin("in_{}".format(row)).rc() + mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) + mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) + self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos]) + + # The mid guarantees we exit the input cell to the right. + driver_wl_pos = self.wordline_driver_inst[port].get_pin("wl_{}".format(row)).lc() + bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).rc() + mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0) + mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1) + self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) + + def route_column_address_lines(self, port): """ Connecting the select lines of column mux to the address bus """ if not self.col_addr_size>0: return - - if self.col_addr_size == 1: # Connect to sel[0] and sel[1] decode_names = ["Zb", "Z"] # The Address LSB - self.copy_layout_pin(self.col_decoder_inst[0], "A", "addr0[0]") - + self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port)) + elif self.col_addr_size > 1: decode_names = [] for i in range(self.num_col_addr_lines): - decode_names.append("out[{}]".format(i)) + decode_names.append("out_{}".format(i)) for i in range(self.col_addr_size): - decoder_name = "in[{}]".format(i) - addr_name = "addr0[{}]".format(i) - self.copy_layout_pin(self.col_decoder_inst[0], decoder_name, addr_name) - + decoder_name = "in_{}".format(i) + addr_name = "addr{0}_{1}".format(port,i) + self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) - # This will do a quick "river route" on two layers. - # When above the top select line it will offset "inward" again to prevent conflicts. - # This could be done on a single layer, but we follow preferred direction rules for later routing. - top_y_offset = self.col_mux_array_inst[0].get_pin("sel[{}]".format(self.num_col_addr_lines-1)).cy() - for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)): - mux_name = "sel[{}]".format(i) - mux_addr_pos = self.col_mux_array_inst[0].get_pin(mux_name).lc() - - decode_out_pos = self.col_decoder_inst[0].get_pin(decode_name).center() + if port%2: + offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0) + else: + offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0) - # To get to the edge of the decoder and one track out - delta_offset = self.col_decoder_inst[0].rx() - decode_out_pos.x + self.m2_pitch - if decode_out_pos.y > top_y_offset: - mid1_pos = vector(decode_out_pos.x + delta_offset + i*self.m2_pitch,decode_out_pos.y) - else: - mid1_pos = vector(decode_out_pos.x + delta_offset + (self.num_col_addr_lines-i)*self.m2_pitch,decode_out_pos.y) - mid2_pos = vector(mid1_pos.x,mux_addr_pos.y) - #self.add_wire(("metal1","via1","metal2"),[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) - self.add_path("metal1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) - - - + decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names] + + sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] + column_mux_pins = [self.column_mux_array_inst[port].get_pin(x) for x in sel_names] + + route_map = list(zip(decode_pins, column_mux_pins)) + self.create_vertical_channel_route(route_map, offset) def add_lvs_correspondence_points(self): @@ -851,7 +1138,7 @@ class bank(design.design): """ # Add the wordline names for i in range(self.num_rows): - wl_name = "wl[{}]".format(i) + wl_name = "wl_{}".format(i) wl_pin = self.bitcell_array_inst.get_pin(wl_name) self.add_label(text=wl_name, layer="metal1", @@ -859,8 +1146,8 @@ class bank(design.design): # Add the bitline names for i in range(self.num_cols): - bl_name = "bl[{}]".format(i) - br_name = "br[{}]".format(i) + bl_name = "bl_{}".format(i) + br_name = "br_{}".format(i) bl_pin = self.bitcell_array_inst.get_pin(bl_name) br_pin = self.bitcell_array_inst.get_pin(br_name) self.add_label(text=bl_name, @@ -872,67 +1159,118 @@ class bank(design.design): # # Add the data output names to the sense amp output # for i in range(self.word_size): - # data_name = "data[{}]".format(i) + # data_name = "data_{}".format(i) # data_pin = self.sense_amp_array_inst.get_pin(data_name) - # self.add_label(text="sa_out[{}]".format(i), + # self.add_label(text="sa_out_{}".format(i), # layer="metal2", # offset=data_pin.center()) # Add labels on the decoder - for i in range(self.word_size): - data_name = "dec_out[{}]".format(i) - pin_name = "in[{}]".format(i) - data_pin = self.wordline_driver_inst[0].get_pin(pin_name) - self.add_label(text=data_name, - layer="metal1", - offset=data_pin.center()) + for port in self.write_ports: + for i in range(self.word_size): + data_name = "dec_out_{}".format(i) + pin_name = "in_{}".format(i) + data_pin = self.wordline_driver_inst[port].get_pin(pin_name) + self.add_label(text=data_name, + layer="metal1", + offset=data_pin.center()) - def route_control_lines(self): + def route_control_lines(self, port): """ Route the control lines of the entire bank """ # Make a list of tuples that we will connect. # From control signal to the module pin # Connection from the central bus to the main control block crosses # pre-decoder and this connection is in metal3 + write_inst = 0 + read_inst = 0 + connection = [] - connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst[0].get_pin("en").lc())) - connection.append((self.prefix+"w_en0", self.write_driver_array_inst[0].get_pin("en").lc())) - connection.append((self.prefix+"s_en0", self.sense_amp_array_inst[0].get_pin("en").lc())) - + if port in self.read_ports: + connection.append((self.prefix+"p_en_bar{}".format(port), self.precharge_array_inst[port].get_pin("en_bar").lc())) + + if port in self.write_ports: + connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc())) + + if port in self.read_ports: + connection.append((self.prefix+"s_en{}".format(port), self.sense_amp_array_inst[port].get_pin("en").lc())) + for (control_signal, pin_pos) in connection: - control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y) + control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y) self.add_path("metal1", [control_pos, pin_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=control_pos, rotate=90) # clk to wordline_driver - control_signal = self.prefix+"clk_buf" - pin_pos = self.wordline_driver_inst[0].get_pin("en").uc() - mid_pos = pin_pos + vector(0,self.m1_pitch) - control_x_offset = self.bus_xoffset[control_signal].x - control_pos = vector(control_x_offset + self.m1_width, mid_pos.y) + control_signal = self.prefix+"wl_en{}".format(port) + if port%2: + pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").uc() + mid_pos = pin_pos + vector(0,self.m2_gap) # to route down to the top of the bus + else: + pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").bc() + mid_pos = pin_pos - vector(0,self.m2_gap) # to route down to the top of the bus + control_x_offset = self.bus_xoffset[port][control_signal].x + control_pos = vector(control_x_offset, mid_pos.y) self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) - control_via_pos = vector(control_x_offset, mid_pos.y) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=control_via_pos, + offset=control_pos, rotate=90) - - def analytical_delay(self, slew, load): + def analytical_delay(self, vdd, slew, load): """ return analytical delay of the bank""" + results = [] + decoder_delay = self.row_decoder.analytical_delay(slew, self.wordline_driver.input_load()) word_driver_delay = self.wordline_driver.analytical_delay(decoder_delay.slew, self.bitcell_array.input_load()) + #FIXME: Array delay is the same for every port. bitcell_array_delay = self.bitcell_array.analytical_delay(word_driver_delay.slew) - bl_t_data_out_delay = self.sense_amp_array.analytical_delay(bitcell_array_delay.slew, - self.bitcell_array.output_load()) - # output load of bitcell_array is set to be only small part of bl for sense amp. + #This also essentially creates the same delay for each port. Good structure, no substance + for port in self.all_ports: + if self.words_per_row > 1: + column_mux_delay = self.column_mux_array[port].analytical_delay(vdd, bitcell_array_delay.slew, + self.sense_amp_array.input_load()) + else: + column_mux_delay = self.return_delay(delay = 0.0, slew=word_driver_delay.slew) + + bl_t_data_out_delay = self.sense_amp_array.analytical_delay(column_mux_delay.slew, + self.bitcell_array.output_load()) + # output load of bitcell_array is set to be only small part of bl for sense amp. + results.append(decoder_delay + word_driver_delay + bitcell_array_delay + column_mux_delay + bl_t_data_out_delay) - result = decoder_delay + word_driver_delay + bitcell_array_delay + bl_t_data_out_delay - return result + return results + + def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): + """Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline""" + #Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption + stage_effort_list = [] + wordline_cout = self.bitcell_array.get_wordline_cin() + external_cout + stage_effort_list += self.wordline_driver.determine_wordline_stage_efforts(wordline_cout,inp_is_rise) + return stage_effort_list + + def get_wl_en_cin(self): + """Get the relative capacitance of all the clk connections in the bank""" + #wl_en only used in the wordline driver. + total_clk_cin = self.wordline_driver.get_wl_en_cin() + return total_clk_cin + + def get_clk_bar_cin(self): + """Get the relative capacitance of all the clk_bar connections in the bank""" + #Current bank only uses clock bar (clk_buf_bar) as an enable for the precharge array. + + #Precharges are the all the same in Mulitport, one is picked + port = self.read_ports[0] + total_clk_bar_cin = self.precharge_array[port].get_en_cin() + return total_clk_bar_cin + + def get_sen_cin(self): + """Get the relative capacitance of all the sense amp enable connections in the bank""" + #Current bank only uses sen as an enable for the sense amps. + total_sen_cin = self.sense_amp_array.get_en_cin() + return total_sen_cin \ No newline at end of file diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 337deb48..7141de2a 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -15,9 +15,11 @@ class bank_select(design.design): banks are created in upper level SRAM module """ - def __init__(self, name="bank_select"): + def __init__(self, name="bank_select", port="rw"): design.design.__init__(self, name) + self.port = port + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -25,12 +27,12 @@ class bank_select(design.design): def create_netlist(self): self.add_pins() self.add_modules() - self.create_modules() + self.create_instances() def create_layout(self): self.calculate_module_offsets() - self.place_modules() - self.route_modules() + self.place_instances() + self.route_instances() self.DRC_LVS() @@ -38,10 +40,17 @@ class bank_select(design.design): def add_pins(self): # Number of control lines in the bus - self.num_control_lines = 4 + if self.port == "rw": + self.num_control_lines = 4 + else: + self.num_control_lines = 3 # The order of the control signals on the control bus: # FIXME: Update for multiport (these names are not right) - self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"] + self.input_control_signals = ["clk_buf", "clk_buf_bar"] + if (self.port == "rw") or (self.port == "w"): + self.input_control_signals.append("w_en") + if (self.port == "rw") or (self.port == "r"): + self.input_control_signals.append("s_en") # These will be outputs of the gaters if this is multibank self.control_signals = ["gated_"+str for str in self.input_control_signals] @@ -53,24 +62,34 @@ class bank_select(design.design): def add_modules(self): """ Create modules for later instantiation """ + from importlib import reload + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.bitcell = self.mod_bitcell() + + height = self.bitcell.height + drc("poly_to_active") + # 1x Inverter - self.inv = pinv() - self.add_mod(self.inv) + self.inv_sel = pinv(height=height) + self.add_mod(self.inv_sel) # 4x Inverter - self.inv4x = pinv(4) + self.inv = self.inv4x = pinv(4) self.add_mod(self.inv4x) - self.nor2 = pnor2() + self.nor2 = pnor2(height=height) self.add_mod(self.nor2) + + self.inv4x_nor = pinv(size=4, height=height) + self.add_mod(self.inv4x_nor) self.nand2 = pnand2() self.add_mod(self.nand2) def calculate_module_offsets(self): - self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc["pwell_to_nwell"] - self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc["pwell_to_nwell"] + self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") + self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") self.xoffset_inv = max(self.xoffset_nand + self.nand2.width, self.xoffset_nor + self.nor2.width) self.xoffset_bank_sel_inv = 0 self.xoffset_inputs = 0 @@ -80,10 +99,10 @@ class bank_select(design.design): self.height = self.yoffset_maxpoint + 2*self.m1_pitch self.width = self.xoffset_inv + self.inv4x.width - def create_modules(self): + def create_instances(self): self.bank_sel_inv=self.add_inst(name="bank_sel_inv", - mod=self.inv) + mod=self.inv_sel) self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"]) self.logic_inst = [] @@ -107,6 +126,14 @@ class bank_select(design.design): "vdd", "gnd"]) + # They all get inverters on the output + self.inv_inst.append(self.add_inst(name=name_inv, + mod=self.inv4x_nor)) + self.connect_inst([gated_name+"_temp_bar", + gated_name, + "vdd", + "gnd"]) + # the rest are AND (nand2+inv) gates else: self.logic_inst.append(self.add_inst(name=name_nand, @@ -117,15 +144,15 @@ class bank_select(design.design): "vdd", "gnd"]) - # They all get inverters on the output - self.inv_inst.append(self.add_inst(name=name_inv, - mod=self.inv4x)) - self.connect_inst([gated_name+"_temp_bar", - gated_name, - "vdd", - "gnd"]) + # They all get inverters on the output + self.inv_inst.append(self.add_inst(name=name_inv, + mod=self.inv4x)) + self.connect_inst([gated_name+"_temp_bar", + gated_name, + "vdd", + "gnd"]) - def place_modules(self): + def place_instances(self): # bank select inverter self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0) @@ -140,7 +167,11 @@ class bank_select(design.design): input_name = self.input_control_signals[i] - y_offset = self.inv.height * i + if i == 0: + y_offset = 0 + else: + y_offset = self.inv4x_nor.height + self.inv.height * (i-1) + if i%2: y_offset += self.inv.height mirror = "MX" @@ -164,7 +195,7 @@ class bank_select(design.design): mirror=mirror) - def route_modules(self): + def route_instances(self): # bank_sel is vertical wire bank_sel_inv_pin = self.bank_sel_inv.get_pin("A") diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 0eb1fbf3..6032fc19 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -4,7 +4,7 @@ from tech import drc, spice from vector import vector from globals import OPTS - +unique_id = 1 class bitcell_array(design.design): """ @@ -12,8 +12,13 @@ class bitcell_array(design.design): and word line is connected by abutment. Connects the word lines and bit lines. """ + unique_id = 1 + + def __init__(self, cols, rows, name=""): - def __init__(self, cols, rows, name="bitcell_array"): + if name == "": + name = "bitcell_array_{0}x{1}_{2}".format(rows,cols,bitcell_array.unique_id) + bitcell_array.unique_id += 1 design.design.__init__(self, name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) @@ -34,12 +39,12 @@ class bitcell_array(design.design): """ Create and connect the netlist """ self.add_modules() self.add_pins() - self.create_modules() + self.create_instances() def create_layout(self): # We increase it by a well enclosure so the precharges don't overlap our wells - self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] + self.m1_width + self.height = self.row_size*self.cell.height + drc("well_enclosure_active") + self.m1_width self.width = self.column_size*self.cell.width + self.m1_width xoffset = 0.0 @@ -69,10 +74,10 @@ class bitcell_array(design.design): column_list = self.cell.list_all_bitline_names() for col in range(self.column_size): for cell_column in column_list: - self.add_pin(cell_column+"[{0}]".format(col)) + self.add_pin(cell_column+"_{0}".format(col)) for row in range(self.row_size): for cell_row in row_list: - self.add_pin(cell_row+"[{0}]".format(row)) + self.add_pin(cell_row+"_{0}".format(row)) self.add_pin("vdd") self.add_pin("gnd") @@ -85,7 +90,7 @@ class bitcell_array(design.design): self.cell = self.mod_bitcell() self.add_mod(self.cell) - def create_modules(self): + def create_instances(self): """ Create the module instances used in this design """ self.cell_inst = {} for col in range(self.column_size): @@ -105,7 +110,7 @@ class bitcell_array(design.design): for col in range(self.column_size): for cell_column in column_list: bl_pin = self.cell_inst[0,col].get_pin(cell_column) - self.add_layout_pin(text=cell_column+"[{0}]".format(col), + self.add_layout_pin(text=cell_column+"_{0}".format(col), layer="metal2", offset=bl_pin.ll(), width=bl_pin.width(), @@ -118,7 +123,7 @@ class bitcell_array(design.design): for row in range(self.row_size): for cell_row in row_list: wl_pin = self.cell_inst[row,0].get_pin(cell_row) - self.add_layout_pin(text=cell_row+"[{0}]".format(row), + self.add_layout_pin(text=cell_row+"_{0}".format(row), layer="metal1", offset=wl_pin.ll(), width=self.width, @@ -127,40 +132,14 @@ class bitcell_array(design.design): # increments to the next row height offset.y += self.cell.height - # For every second row and column, add a via for vdd + # For every second row and column, add a via for gnd and vdd for row in range(self.row_size): for col in range(self.column_size): inst = self.cell_inst[row,col] - for vdd_pin in inst.get_pins("vdd"): - # Drop to M1 if needed - if vdd_pin.layer == "metal1": - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=vdd_pin.center(), - rotate=90) - # Always drop to M2 - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=vdd_pin.center()) - self.add_layout_pin_rect_center(text="vdd", - layer="metal3", - offset=vdd_pin.center()) - - - # For every second row and column (+1), add a via for gnd - for row in range(self.row_size): - for col in range(self.column_size): - inst = self.cell_inst[row,col] - for gnd_pin in inst.get_pins("gnd"): - # Drop to M1 if needed - if gnd_pin.layer == "metal1": - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=gnd_pin.center(), - rotate=90) - # Always drop to M2 - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=gnd_pin.center()) - self.add_layout_pin_rect_center(text="gnd", - layer="metal3", - offset=gnd_pin.center()) + for pin_name in ["vdd", "gnd"]: + for pin in inst.get_pins(pin_name): + self.add_power_pin(pin_name, pin.center(), 0, pin.layer) + def analytical_delay(self, slew, load=0): from tech import drc @@ -199,13 +178,21 @@ class bitcell_array(design.design): return total_power def gen_wl_wire(self): - wl_wire = self.generate_rc_net(int(self.column_size), self.width, drc["minwidth_metal1"]) + if OPTS.netlist_only: + width = 0 + else: + width = self.width + wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_metal1")) wl_wire.wire_c = 2*spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell return wl_wire def gen_bl_wire(self): + if OPTS.netlist_only: + height = 0 + else: + height = self.height bl_pos = 0 - bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), self.height, drc["minwidth_metal1"]) + bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_metal1")) bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell return bl_wire @@ -217,3 +204,10 @@ class bitcell_array(design.design): def input_load(self): wl_wire = self.gen_wl_wire() return wl_wire.return_input_cap() + + def get_wordline_cin(self): + """Get the relative input capacitance from the wordline connections in all the bitcell""" + #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns + bitcell_wl_cin = self.cell.get_wl_cin() + total_cin = bitcell_wl_cin * self.column_size + return total_cin \ No newline at end of file diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 78223d5f..c30d91a0 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -4,26 +4,44 @@ from tech import drc, parameter import debug import contact from pinv import pinv +from pbuf import pbuf +from pand2 import pand2 from pnand2 import pnand2 -from pnand3 import pnand3 from pinvbuf import pinvbuf -from dff_inv import dff_inv -from dff_inv_array import dff_inv_array +from dff_buf import dff_buf +from dff_buf_array import dff_buf_array import math from vector import vector from globals import OPTS +import logical_effort class control_logic(design.design): """ Dynamically generated Control logic for the total SRAM circuit. """ - def __init__(self, num_rows): + def __init__(self, num_rows, words_per_row, sram=None, port_type="rw"): """ Constructor """ - design.design.__init__(self, "control_logic") - debug.info(1, "Creating {}".format(self.name)) - + name = "control_logic_" + port_type + design.design.__init__(self, name) + debug.info(1, "Creating {}".format(name)) + self.num_rows = num_rows + self.words_per_row = words_per_row + self.port_type = port_type + + self.enable_delay_chain_resizing = False + + #This is needed to resize the delay chain. Likely to be changed at some point. + self.sram=sram + #self.sram=None #disable re-sizing for debugging, FIXME: resizing is not working, needs to be adjusted for new control logic. + self.wl_timing_tolerance = 1 #Determines how much larger the sen delay should be. Accounts for possible error in model. + self.parasitic_inv_delay = parameter["min_inv_para_delay"] #Keeping 0 for now until further testing. + + if self.port_type == "rw": + self.num_control_signals = 2 + else: + self.num_control_signals = 1 self.create_netlist() if not OPTS.netlist_only: @@ -33,16 +51,13 @@ class control_logic(design.design): self.setup_signal_busses() self.add_pins() self.add_modules() - self.create_modules() + self.create_instances() def create_layout(self): """ Create layout and route between modules """ - self.route_rails() - self.place_modules() + self.place_instances() self.route_all() - - self.add_lvs_correspondence_points() - + #self.add_lvs_correspondence_points() self.DRC_LVS() @@ -58,212 +73,626 @@ class control_logic(design.design): def add_modules(self): """ Add all the required modules """ - dff = dff_inv() + dff = dff_buf() dff_height = dff.height - self.ctrl_dff_array = dff_inv_array(rows=2,columns=1) + self.ctrl_dff_array = dff_buf_array(rows=self.num_control_signals,columns=1) self.add_mod(self.ctrl_dff_array) - self.nand2 = pnand2(height=dff_height) - self.add_mod(self.nand2) - self.nand3 = pnand3(height=dff_height) - self.add_mod(self.nand3) - + self.and2 = pand2(size=4,height=dff_height) + self.add_mod(self.and2) + # Special gates: inverters for buffering # 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.clkbuf = pbuf(size=clock_driver_size, height=dff_height) self.add_mod(self.clkbuf) + + self.buf16 = pbuf(size=16, height=dff_height) + self.add_mod(self.buf16) + + self.buf8 = pbuf(size=8, height=dff_height) + self.add_mod(self.buf8) + self.inv = self.inv1 = pinv(size=1, height=dff_height) self.add_mod(self.inv1) - self.inv2 = pinv(size=4, height=dff_height) - self.add_mod(self.inv2) - self.inv8 = pinv(size=16, height=dff_height) + + self.inv8 = pinv(size=8, height=dff_height) self.add_mod(self.inv8) + + # self.inv2 = pinv(size=4, height=dff_height) + # self.add_mod(self.inv2) + #self.inv16 = pinv(size=16, height=dff_height) + #self.add_mod(self.inv16) - from importlib import reload - c = reload(__import__(OPTS.replica_bitline)) - replica_bitline = getattr(c, OPTS.replica_bitline) - # FIXME: These should be tuned according to the size! - delay_stages = 4 # Must be non-inverting + if (self.port_type == "rw") or (self.port_type == "r"): + from importlib import reload + c = reload(__import__(OPTS.replica_bitline)) + replica_bitline = getattr(c, OPTS.replica_bitline) + + delay_stages_heuristic, delay_fanout_heuristic = self.get_heuristic_delay_chain_size() + bitcell_loads = int(math.ceil(self.num_rows / 2.0)) + self.replica_bitline = replica_bitline([delay_fanout_heuristic]*delay_stages_heuristic, bitcell_loads, name="replica_bitline_"+self.port_type) + + if self.sram != None: + self.set_sen_wl_delays() + + if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_total_timing_match(): #check condition based on resizing method + #This resizes to match fall and rise delays, can make the delay chain weird sizes. + # stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) + # self.replica_bitline = replica_bitline(stage_list, bitcell_loads, name="replica_bitline_resized_"+self.port_type) + + #This resizes based on total delay. + delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) + self.replica_bitline = replica_bitline([delay_fanout]*delay_stages, bitcell_loads, name="replica_bitline_resized_"+self.port_type) + + self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing + + self.add_mod(self.replica_bitline) + + def get_heuristic_delay_chain_size(self): + """Use a basic heuristic to determine the size of the delay chain used for the Sense Amp Enable """ + # FIXME: These should be tuned according to the additional size parameters delay_fanout = 3 # This can be anything >=2 - bitcell_loads = int(math.ceil(self.num_rows / 5.0)) - self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads) - self.add_mod(self.replica_bitline) - - + # Delay stages Must be non-inverting + if self.words_per_row >= 4: + delay_stages = 8 + elif self.words_per_row == 2: + delay_stages = 6 + else: + delay_stages = 4 + + return (delay_stages, delay_fanout) + + def set_sen_wl_delays(self): + """Set delays for wordline and sense amp enable""" + self.wl_delay_rise,self.wl_delay_fall = self.get_delays_to_wl() + self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() + self.wl_delay = self.wl_delay_rise+self.wl_delay_fall + self.sen_delay = self.sen_delay_rise+self.sen_delay_fall + + def does_sen_rise_fall_timing_match(self): + """Compare the relative rise/fall delays of the sense amp enable and wordline""" + self.set_sen_wl_delays() + #This is not necessarily more reliable than total delay in some cases. + if (self.wl_delay_rise*self.wl_timing_tolerance >= self.sen_delay_rise or + self.wl_delay_fall*self.wl_timing_tolerance >= self.sen_delay_fall): + return False + else: + return True + + def does_sen_total_timing_match(self): + """Compare the total delays of the sense amp enable and wordline""" + self.set_sen_wl_delays() + #The sen delay must always be bigger than than the wl delay. This decides how much larger the sen delay must be before + #a re-size is warranted. + if self.wl_delay*self.wl_timing_tolerance >= self.sen_delay: + return False + else: + return True + + def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout): + """Determine the size of the delay chain used for the Sense Amp Enable using path delays""" + from math import ceil + previous_delay_chain_delay = (previous_fanout+1+self.parasitic_inv_delay)*previous_stages + debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay)) + + delay_fanout = 3 # This can be anything >=2 + #The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each + #inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value + required_delay = self.wl_delay*self.wl_timing_tolerance - (self.sen_delay-previous_delay_chain_delay) + debug.check(required_delay > 0, "Cannot size delay chain to have negative delay") + delay_stages = ceil(required_delay/(delay_fanout+1+self.parasitic_inv_delay)) + if delay_stages%2 == 1: #force an even number of stages. + delay_stages+=1 + #Fanout can be varied as well but is a little more complicated but potentially optimal. + debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay)) + return (delay_stages, delay_fanout) + + def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout): + """Determine the size of the delay chain used for the Sense Amp Enable using path delays""" + + previous_delay_chain_delay = (previous_fanout+1+self.parasitic_inv_delay)*previous_stages + debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay)) + + fanout_rise = fanout_fall = 2 # This can be anything >=2 + #The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each + #inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value + required_delay_fall = self.wl_delay_fall*self.wl_timing_tolerance - (self.sen_delay_fall-previous_delay_chain_delay/2) + required_delay_rise = self.wl_delay_rise*self.wl_timing_tolerance - (self.sen_delay_rise-previous_delay_chain_delay/2) + debug.info(2,"Required delays from chain: fall={}, rise={}".format(required_delay_fall,required_delay_rise)) + + #The stages need to be equal (or at least a even number of stages with matching rise/fall delays) + while True: + stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,fanout_fall) + stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,fanout_rise) + debug.info(1,"Fall stages={}, rise stages={}".format(stages_fall,stages_rise)) + if stages_fall == stages_rise: + break + elif abs(stages_fall-stages_rise) == 1: + break + #There should also be a condition to make sure the fanout does not get too large. + #Otherwise, increase the fanout of delay with the most stages, calculate new stages + elif stages_fall>stages_rise: + fanout_fall+=1 + else: + fanout_rise+=1 + + total_stages = max(stages_fall,stages_rise)*2 + debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall)) + + #Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage. + stage_list = [fanout_fall if i%2==0 else fanout_rise for i in range(total_stages)] + return stage_list + + def calculate_stages_with_fixed_fanout(self, required_delay, fanout): + from math import ceil + #Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay + if required_delay <= 3+self.parasitic_inv_delay: #3 is the minimum delay per stage (with pinv=0). + return 1 + delay_stages = ceil(required_delay/(fanout+1+self.parasitic_inv_delay)) + return delay_stages + + def calculate_stage_list(self, total_stages, fanout_rise, fanout_fall): + """Produces a list of fanouts which determine the size of the delay chain. List length is the number of stages. + Assumes the first stage is falling. + """ + stage_list = [] + for i in range(total_stages): + if i%2 == 0: + stage_list.append() + def setup_signal_busses(self): """ Setup bus names, determine the size of the busses etc """ # List of input control signals - self.input_list =["csb","web0"] - self.dff_output_list =["cs_bar", "cs", "we_bar", "we"] + if self.port_type == "rw": + self.input_list = ["csb", "web"] + else: + self.input_list = ["csb"] + + if self.port_type == "rw": + self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"] + else: + self.dff_output_list = ["cs_bar", "cs"] + # list of output control signals (for making a vertical bus) - self.internal_bus_list = ["clk_buf", "clk_buf_bar", "we", "cs"] + if self.port_type == "rw": + self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "we", "clk_buf", "we_bar", "cs"] + elif self.port_type == "r": + self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"] + else: + self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"] # leave space for the bus plus one extra space self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch + # Outputs to the bank - self.output_list = ["s_en0", "w_en0", "clk_buf_bar", "clk_buf"] + if self.port_type == "rw": + self.output_list = ["s_en", "w_en", "p_en_bar"] + elif self.port_type == "r": + self.output_list = ["s_en", "p_en_bar"] + else: + self.output_list = ["w_en"] + self.output_list.append("wl_en") + self.output_list.append("clk_buf") + self.supply_list = ["vdd", "gnd"] def route_rails(self): """ Add the input signal inverted tracks """ - height = 4*self.inv1.height - self.m2_pitch + height = self.control_logic_center.y - self.m2_pitch offset = vector(self.ctrl_dff_array.width,0) self.rail_offsets = self.create_vertical_bus("metal2", self.m2_pitch, offset, self.internal_bus_list, height) - def create_modules(self): - """ Create all the modules """ + def create_instances(self): + """ Create all the instances """ self.create_dffs() - self.create_clk_row() - self.create_we_row() - # self.create_trien_row() - # self.create_trien_bar_row() - self.create_rbl_in_row() - self.create_sen_row() - self.create_rbl() - + self.create_clk_buf_row() + self.create_gated_clk_bar_row() + self.create_gated_clk_buf_row() + self.create_wlen_row() + if (self.port_type == "rw") or (self.port_type == "w"): + self.create_wen_row() + if self.port_type == "rw": + self.create_rbl_in_row() + if (self.port_type == "rw") or (self.port_type == "r"): + self.create_pen_row() + self.create_sen_row() + self.create_rbl() - def place_modules(self): - """ Place all the modules """ + def place_instances(self): + """ Place all the instances """ # Keep track of all right-most instances to determine row boundary # and add the vdd/gnd pins self.row_end_inst = [] - # Add the control flops on the left of the bus self.place_dffs() - # Add the logic on the right of the bus - self.place_clk_row(row=0) # clk is a double-high cell - self.place_we_row(row=2) - # self.place_trien_row(row=3) - # self.place_trien_bar_row(row=4) - self.place_rbl_in_row(row=3) - self.place_sen_row(row=4) - self.place_rbl(row=5) + # All of the control logic is placed to the right of the DFFs and bus + self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + row = 0 + # Add the logic on the right of the bus + self.place_clk_buf_row(row) + row += 1 + self.place_gated_clk_bar_row(row) + row += 1 + self.place_gated_clk_buf_row(row) + row += 1 + self.place_wlen_row(row) + row += 1 + if (self.port_type == "rw") or (self.port_type == "w"): + self.place_wen_row(row) + height = self.w_en_inst.uy() + control_center_y = self.w_en_inst.uy() + row += 1 + if self.port_type == "rw": + self.place_rbl_in_row(row) + row += 1 + if (self.port_type == "rw") or (self.port_type == "r"): + self.place_pen_row(row) + row += 1 + self.place_sen_row(row) + row += 1 + self.place_rbl(row) + height = self.rbl_inst.uy() + control_center_y = self.rbl_inst.by() - # This offset is used for placement of the control logic in - # the SRAM level. - self.control_logic_center = vector(self.ctrl_dff_inst.rx(), self.rbl_inst.by()) + # This offset is used for placement of the control logic in the SRAM level. + self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y) # Extra pitch on top and right - self.height = self.rbl_inst.uy() + self.m3_pitch + self.height = height + 2*self.m1_pitch # Max of modules or logic rows - self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch - + self.width = max([inst.rx() for inst in self.row_end_inst]) + if (self.port_type == "rw") or (self.port_type == "r"): + self.width = max(self.rbl_inst.rx() , self.width) + self.width += self.m2_pitch def route_all(self): """ Routing between modules """ + self.route_rails() self.route_dffs() - #self.route_trien() - #self.route_trien_bar() - self.route_rbl_in() - self.route_wen() - self.route_sen() - self.route_clk() + self.route_wlen() + if (self.port_type == "rw") or (self.port_type == "w"): + self.route_wen() + if (self.port_type == "rw") or (self.port_type == "r"): + self.route_rbl_in() + self.route_pen() + self.route_sen() + self.route_clk_buf() + self.route_gated_clk_bar() + self.route_gated_clk_buf() self.route_supply() def create_rbl(self): """ Create the replica bitline """ + if self.port_type == "r": + input_name = "gated_clk_bar" + else: + input_name = "rbl_in" self.rbl_inst=self.add_inst(name="replica_bitline", mod=self.replica_bitline) - self.connect_inst(["rbl_in", "pre_s_en", "vdd", "gnd"]) + self.connect_inst([input_name, "pre_s_en", "vdd", "gnd"]) def place_rbl(self,row): """ Place the replica bitline """ - y_off = row * self.inv1.height + 2*self.m1_pitch + y_off = row * self.and2.height + 2*self.m1_pitch # Add the RBL above the rows # Add to the right of the control rows and routing channel - self.replica_bitline_offset = vector(0, y_off) - self.rbl_inst.place(self.replica_bitline_offset) + offset = vector(0, y_off) + self.rbl_inst.place(offset) - def create_clk_row(self): - """ Create the multistage clock buffer """ + def create_clk_buf_row(self): + """ Create the multistage and gated clock buffer """ self.clkbuf_inst = self.add_inst(name="clkbuf", mod=self.clkbuf) - self.connect_inst(["clk","clk_buf_bar","clk_buf","vdd","gnd"]) - - def place_clk_row(self,row): - """ Place the multistage clock buffer below the control flops """ - x_off = self.ctrl_dff_array.width + self.internal_bus_width - (y_off,mirror)=self.get_offset(row) - clkbuf_offset = vector(x_off,y_off) - self.clkbuf_inst.place(clkbuf_offset) - self.row_end_inst.append(self.clkbuf_inst) + self.connect_inst(["clk","clk_buf","vdd","gnd"]) + def place_clk_buf_row(self,row): + """ Place the multistage clock buffer below the control flops """ + x_off = self.control_x_offset + (y_off,mirror)=self.get_offset(row) + + offset = vector(x_off,y_off) + self.clkbuf_inst.place(offset, mirror) + + self.row_end_inst.append(self.clkbuf_inst) + + def route_clk_buf(self): + clk_pin = self.clkbuf_inst.get_pin("A") + clk_pos = clk_pin.center() + self.add_layout_pin_segment_center(text="clk", + layer="metal2", + start=clk_pos, + end=clk_pos.scale(1,0)) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=clk_pos) + + + clkbuf_map = zip(["Z"], ["clk_buf"]) + self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + # The pin is on M1, so we need another via as well + self.add_via_center(layers=("metal1","via1","metal2"), + offset=self.clkbuf_inst.get_pin("Z").center()) + + + self.connect_output(self.clkbuf_inst, "Z", "clk_buf") + + def create_gated_clk_bar_row(self): + self.clk_bar_inst = self.add_inst(name="inv_clk_bar", + mod=self.inv) + self.connect_inst(["clk_buf","clk_bar","vdd","gnd"]) + + self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar", + mod=self.and2) + self.connect_inst(["cs","clk_bar","gated_clk_bar","vdd","gnd"]) + + def place_gated_clk_bar_row(self,row): + """ Place the gated clk logic below the control flops """ + x_off = self.control_x_offset + (y_off,mirror)=self.get_offset(row) + + offset = vector(x_off,y_off) + self.clk_bar_inst.place(offset, mirror) + + x_off += self.inv.width + + offset = vector(x_off,y_off) + self.gated_clk_bar_inst.place(offset, mirror) + + self.row_end_inst.append(self.gated_clk_bar_inst) + + def route_gated_clk_bar(self): + clkbuf_map = zip(["A"], ["clk_buf"]) + self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets) + + out_pos = self.clk_bar_inst.get_pin("Z").center() + in_pos = self.gated_clk_bar_inst.get_pin("B").center() + mid1 = vector(in_pos.x,out_pos.y) + self.add_path("metal1",[out_pos, mid1, in_pos]) + + # This is the second gate over, so it needs to be on M3 + clkbuf_map = zip(["A"], ["cs"]) + self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + # The pin is on M1, so we need another via as well + self.add_via_center(layers=("metal1","via1","metal2"), + offset=self.gated_clk_bar_inst.get_pin("A").center()) + + + # This is the second gate over, so it needs to be on M3 + clkbuf_map = zip(["Z"], ["gated_clk_bar"]) + self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + # The pin is on M1, so we need another via as well + self.add_via_center(layers=("metal1","via1","metal2"), + offset=self.gated_clk_bar_inst.get_pin("Z").center()) + + def create_gated_clk_buf_row(self): + self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf", + mod=self.and2) + self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"]) + + def place_gated_clk_buf_row(self,row): + """ Place the gated clk logic below the control flops """ + x_off = self.control_x_offset + (y_off,mirror)=self.get_offset(row) + + offset = vector(x_off,y_off) + self.gated_clk_buf_inst.place(offset, mirror) + + self.row_end_inst.append(self.gated_clk_buf_inst) + + def route_gated_clk_buf(self): + clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"]) + self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets) + + + clkbuf_map = zip(["Z"], ["gated_clk_buf"]) + self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + # The pin is on M1, so we need another via as well + self.add_via_center(layers=("metal1","via1","metal2"), + offset=self.gated_clk_buf_inst.get_pin("Z").center()) + + def create_wlen_row(self): + # input pre_p_en, output: wl_en + self.wl_en_inst=self.add_inst(name="buf_wl_en", + mod=self.buf16) + self.connect_inst(["gated_clk_bar", "wl_en", "vdd", "gnd"]) + + def place_wlen_row(self, row): + x_off = self.control_x_offset + (y_off,mirror)=self.get_offset(row) + + offset = vector(x_off, y_off) + self.wl_en_inst.place(offset, mirror) + + self.row_end_inst.append(self.wl_en_inst) + + def route_wlen(self): + wlen_map = zip(["A"], ["gated_clk_bar"]) + self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.rail_offsets) + self.connect_output(self.wl_en_inst, "Z", "wl_en") def create_rbl_in_row(self): - self.rbl_in_bar_inst=self.add_inst(name="nand3_rbl_in_bar", - mod=self.nand2) - self.connect_inst(["clk_buf_bar", "cs", "rbl_in_bar", "vdd", "gnd"]) - - # input: rbl_in_bar, output: rbl_in - self.rbl_in_inst=self.add_inst(name="inv_rbl_in", - mod=self.inv1) - self.connect_inst(["rbl_in_bar", "rbl_in", "vdd", "gnd"]) + # input: gated_clk_bar, we_bar, output: rbl_in + self.rbl_in_inst=self.add_inst(name="and2_rbl_in", + mod=self.and2) + self.connect_inst(["gated_clk_bar", "we_bar", "rbl_in", "vdd", "gnd"]) def place_rbl_in_row(self,row): - x_off = self.ctrl_dff_array.width + self.internal_bus_width + x_off = self.control_x_offset (y_off,mirror)=self.get_offset(row) + offset = vector(x_off, y_off) + self.rbl_in_inst.place(offset, mirror) - self.rbl_in_bar_offset = vector(x_off, y_off) - self.rbl_in_bar_inst.place(offset=self.rbl_in_bar_offset, - mirror=mirror) - x_off += self.nand2.width - - self.rbl_in_offset = vector(x_off, y_off) - self.rbl_in_inst.place(offset=self.rbl_in_offset, - mirror=mirror) self.row_end_inst.append(self.rbl_in_inst) + + def route_rbl_in(self): + """ Connect the logic for the rbl_in generation """ + + if self.port_type == "rw": + input_name = "we_bar" + # Connect the NAND gate inputs to the bus + rbl_in_map = zip(["A", "B"], ["gated_clk_bar", "we_bar"]) + self.connect_vertical_bus(rbl_in_map, self.rbl_in_inst, self.rail_offsets) + + + # Connect the output of the precharge enable to the RBL input + if self.port_type == "rw": + out_pos = self.rbl_in_inst.get_pin("Z").center() + else: + out_pos = vector(self.rail_offsets["gated_clk_bar"].x, self.rbl_inst.by()-3*self.m2_pitch) + in_pos = self.rbl_inst.get_pin("en").center() + mid1 = vector(in_pos.x,out_pos.y) + self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, in_pos]) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=out_pos, + rotate=90) + self.add_via_center(layers=("metal2","via2","metal3"), + offset=out_pos, + rotate=90) + + def create_pen_row(self): + if self.port_type == "rw": + # input: gated_clk_bar, we_bar, output: pre_p_en + self.pre_p_en_inst=self.add_inst(name="and2_pre_p_en", + mod=self.and2) + self.connect_inst(["gated_clk_buf", "we_bar", "pre_p_en", "vdd", "gnd"]) + input_name = "pre_p_en" + else: + input_name = "gated_clk_buf" + + # input: pre_p_en, output: p_en_bar + self.p_en_bar_inst=self.add_inst(name="inv_p_en_bar", + mod=self.inv8) + self.connect_inst([input_name, "p_en_bar", "vdd", "gnd"]) + + + def place_pen_row(self,row): + x_off = self.control_x_offset + (y_off,mirror)=self.get_offset(row) + + if self.port_type == "rw": + offset = vector(x_off, y_off) + self.pre_p_en_inst.place(offset, mirror) + + x_off += self.and2.width + + offset = vector(x_off,y_off) + self.p_en_bar_inst.place(offset, mirror) + + self.row_end_inst.append(self.p_en_bar_inst) + + def route_pen(self): + if self.port_type == "rw": + # Connect the NAND gate inputs to the bus + pre_p_en_in_map = zip(["A", "B"], ["gated_clk_buf", "we_bar"]) + self.connect_vertical_bus(pre_p_en_in_map, self.pre_p_en_inst, self.rail_offsets) + + out_pos = self.pre_p_en_inst.get_pin("Z").center() + in_pos = self.p_en_bar_inst.get_pin("A").lc() + mid1 = vector(out_pos.x,in_pos.y) + self.add_wire(("metal1","via1","metal2"),[out_pos,mid1,in_pos]) + else: + in_map = zip(["A"], ["gated_clk_buf"]) + self.connect_vertical_bus(in_map, self.p_en_bar_inst, self.rail_offsets) + + self.connect_output(self.p_en_bar_inst, "Z", "p_en_bar") def create_sen_row(self): """ Create the sense enable buffer. """ - # input: pre_s_en, output: pre_s_en_bar - self.pre_s_en_bar_inst=self.add_inst(name="inv_pre_s_en_bar", - mod=self.inv2) - self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"]) - - # BUFFER INVERTERS FOR S_EN - # input: input: pre_s_en_bar, output: s_en - self.s_en_inst=self.add_inst(name="inv_s_en", - mod=self.inv8) - self.connect_inst(["pre_s_en_bar", "s_en0", "vdd", "gnd"]) + # BUFFER FOR S_EN + # input: pre_s_en, output: s_en + self.s_en_inst=self.add_inst(name="buf_s_en", + mod=self.buf8) + self.connect_inst(["pre_s_en", "s_en", "vdd", "gnd"]) def place_sen_row(self,row): """ The sense enable buffer gets placed to the far right of the row. """ - x_off = self.ctrl_dff_array.width + self.internal_bus_width + x_off = self.control_x_offset (y_off,mirror)=self.get_offset(row) - self.pre_s_en_bar_offset = vector(x_off, y_off) - self.pre_s_en_bar_inst.place(offset=self.pre_s_en_bar_offset, - mirror=mirror) - x_off += self.inv2.width + offset = vector(x_off, y_off) + self.s_en_inst.place(offset, mirror) - self.s_en_offset = vector(x_off, y_off) - self.s_en_inst.place(offset=self.s_en_offset, - mirror=mirror) self.row_end_inst.append(self.s_en_inst) + + def route_sen(self): + + out_pos = self.rbl_inst.get_pin("out").bc() + in_pos = self.s_en_inst.get_pin("A").lc() + mid1 = vector(out_pos.x,in_pos.y) + self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos]) + + self.connect_output(self.s_en_inst, "Z", "s_en") + + + def create_wen_row(self): + # input: we (or cs) output: w_en + if self.port_type == "rw": + input_name = "we" + else: + # No we for write-only reports, so use cs + input_name = "cs" + + # BUFFER FOR W_EN + self.w_en_inst = self.add_inst(name="buf_w_en_buf", + mod=self.buf8) + self.connect_inst([input_name, "w_en", "vdd", "gnd"]) + + + def place_wen_row(self,row): + x_off = self.ctrl_dff_inst.width + self.internal_bus_width + (y_off,mirror)=self.get_offset(row) + + offset = vector(x_off, y_off) + self.w_en_inst.place(offset, mirror) + + self.row_end_inst.append(self.w_en_inst) + + def route_wen(self): + + if self.port_type == "rw": + input_name = "we" + else: + # No we for write-only reports, so use cs + input_name = "cs" + + wen_map = zip(["A"], [input_name]) + self.connect_vertical_bus(wen_map, self.w_en_inst, self.rail_offsets) + + self.connect_output(self.w_en_inst, "Z", "w_en") + + def create_dffs(self): + self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs", + mod=self.ctrl_dff_array) + self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list) + + def place_dffs(self): + self.ctrl_dff_inst.place(vector(0,0)) def route_dffs(self): - """ Route the input inverters """ - - dff_out_map = zip(["dout_bar[{}]".format(i) for i in range(3)], ["cs", "we"]) - self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets) + if self.port_type == "rw": + dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"]) + elif self.port_type == "r": + dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"]) + else: + dff_out_map = zip(["dout_bar_0"], ["cs"]) + self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("metal3", "via2", "metal2")) # Connect the clock rail to the other clock rail in_pos = self.ctrl_dff_inst.get_pin("clk").uc() @@ -274,197 +703,22 @@ class control_logic(design.design): offset=rail_pos, rotate=90) - self.copy_layout_pin(self.ctrl_dff_inst, "din[0]", "csb") - self.copy_layout_pin(self.ctrl_dff_inst, "din[1]", "web0") + self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb") + if (self.port_type == "rw"): + self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web") - - def create_dffs(self): - """ Add the three input DFFs (with inverters) """ - self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs", - mod=self.ctrl_dff_array) - self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list) - - def place_dffs(self): - """ Place the input DFFs (with inverters) """ - self.ctrl_dff_inst.place(vector(0,0)) - - def get_offset(self,row): """ Compute the y-offset and mirroring """ - y_off = row*self.inv1.height + y_off = row*self.and2.height if row % 2: - y_off += self.inv1.height + y_off += self.and2.height mirror="MX" else: mirror="R0" return (y_off,mirror) - def create_we_row(self): - # input: WE, CS output: w_en_bar - self.w_en_bar_inst=self.add_inst(name="nand3_w_en_bar", - mod=self.nand3) - self.connect_inst(["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"]) - - # input: w_en_bar, output: pre_w_en - self.pre_w_en_inst=self.add_inst(name="inv_pre_w_en", - mod=self.inv1) - self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"]) - - # BUFFER INVERTERS FOR W_EN - self.pre_w_en_bar_inst=self.add_inst(name="inv_pre_w_en_bar", - mod=self.inv2) - self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"]) - - self.w_en_inst=self.add_inst(name="inv_w_en2", - mod=self.inv8) - self.connect_inst(["pre_w_en_bar", "w_en0", "vdd", "gnd"]) - - - def place_we_row(self,row): - x_off = self.ctrl_dff_inst.width + self.internal_bus_width - (y_off,mirror)=self.get_offset(row) - - w_en_bar_offset = vector(x_off, y_off) - self.w_en_bar_inst.place(offset=w_en_bar_offset, - mirror=mirror) - x_off += self.nand3.width - - pre_w_en_offset = vector(x_off, y_off) - self.pre_w_en_inst.place(offset=pre_w_en_offset, - mirror=mirror) - x_off += self.inv1.width - - pre_w_en_bar_offset = vector(x_off, y_off) - self.pre_w_en_bar_inst.place(offset=pre_w_en_bar_offset, - mirror=mirror) - x_off += self.inv2.width - - w_en_offset = vector(x_off, y_off) - self.w_en_inst.place(offset=w_en_offset, - mirror=mirror) - x_off += self.inv8.width - - self.row_end_inst.append(self.w_en_inst) - - - def route_rbl_in(self): - """ Connect the logic for the rbl_in generation """ - rbl_in_map = zip(["A", "B"], ["clk_buf_bar", "cs"]) - self.connect_vertical_bus(rbl_in_map, self.rbl_in_bar_inst, self.rail_offsets) - - # Connect the NAND3 output to the inverter - # The pins are assumed to extend all the way to the cell edge - rbl_in_bar_pos = self.rbl_in_bar_inst.get_pin("Z").center() - inv_in_pos = self.rbl_in_inst.get_pin("A").center() - mid1 = vector(inv_in_pos.x,rbl_in_bar_pos.y) - self.add_path("metal1",[rbl_in_bar_pos,mid1,inv_in_pos]) - - # Connect the output to the RBL - rbl_out_pos = self.rbl_in_inst.get_pin("Z").center() - rbl_in_pos = self.rbl_inst.get_pin("en").center() - mid1 = vector(rbl_in_pos.x,rbl_out_pos.y) - self.add_wire(("metal3","via2","metal2"),[rbl_out_pos,mid1,rbl_in_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rbl_out_pos, - rotate=90) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=rbl_out_pos, - rotate=90) - - def connect_rail_from_right(self,inst, pin, rail): - """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ - in_pos = inst.get_pin(pin).center() - rail_pos = vector(self.rail_offsets[rail].x, in_pos.y) - self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail_pos, - rotate=90) - - def connect_rail_from_right_m2m3(self,inst, pin, rail): - """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ - in_pos = inst.get_pin(pin).center() - rail_pos = vector(self.rail_offsets[rail].x, in_pos.y) - self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos]) - # Bring it up to M2 for M2/M3 routing - self.add_via_center(layers=("metal1","via1","metal2"), - offset=in_pos, - rotate=90) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=in_pos, - rotate=90) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=rail_pos, - rotate=90) - - - def connect_rail_from_left(self,inst, pin, rail): - """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ - in_pos = inst.get_pin(pin).lc() - rail_pos = vector(self.rail_offsets[rail].x, in_pos.y) - self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail_pos, - rotate=90) - - def connect_rail_from_left_m2m3(self,inst, pin, rail): - """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ - in_pos = inst.get_pin(pin).lc() - rail_pos = vector(self.rail_offsets[rail].x, in_pos.y) - self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos]) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=in_pos, - rotate=90) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=rail_pos, - rotate=90) - - - def route_wen(self): - 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 - # The pins are assumed to extend all the way to the cell edge - w_en_bar_pos = self.w_en_bar_inst.get_pin("Z").center() - inv_in_pos = self.pre_w_en_inst.get_pin("A").center() - mid1 = vector(inv_in_pos.x,w_en_bar_pos.y) - self.add_path("metal1",[w_en_bar_pos,mid1,inv_in_pos]) - - self.add_path("metal1",[self.pre_w_en_inst.get_pin("Z").center(), self.pre_w_en_bar_inst.get_pin("A").center()]) - self.add_path("metal1",[self.pre_w_en_bar_inst.get_pin("Z").center(), self.w_en_inst.get_pin("A").center()]) - - self.connect_output(self.w_en_inst, "Z", "w_en0") - - def route_sen(self): - rbl_out_pos = self.rbl_inst.get_pin("out").bc() - in_pos = self.pre_s_en_bar_inst.get_pin("A").lc() - mid1 = vector(rbl_out_pos.x,in_pos.y) - self.add_wire(("metal1","via1","metal2"),[rbl_out_pos,mid1,in_pos]) - #s_en_pos = self.s_en.get_pin("Z").lc() - - self.add_path("metal1",[self.pre_s_en_bar_inst.get_pin("Z").center(), self.s_en_inst.get_pin("A").center()]) - - self.connect_output(self.s_en_inst, "Z", "s_en0") - - def route_clk(self): - """ Route the clk and clk_buf_bar signal internally """ - - clk_pin = self.clkbuf_inst.get_pin("A") - self.add_layout_pin_segment_center(text="clk", - layer="metal2", - start=clk_pin.bc(), - end=clk_pin.bc().scale(1,0)) - - clkbuf_map = zip(["Z", "Zb"], ["clk_buf", "clk_buf_bar"]) - self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2")) - - # self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Z", "clk_buf") - # self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Zb", "clk_buf_bar") - self.connect_output(self.clkbuf_inst, "Z", "clk_buf") - self.connect_output(self.clkbuf_inst, "Zb", "clk_buf_bar") - def connect_output(self, inst, pin_name, out_name): """ Create an output pin on the right side from the pin of a given instance. """ @@ -498,9 +752,9 @@ class control_logic(design.design): self.add_power_pin("gnd", pin_loc) self.add_path("metal1", [row_loc, pin_loc]) - - self.copy_layout_pin(self.rbl_inst,"gnd") - self.copy_layout_pin(self.rbl_inst,"vdd") + if (self.port_type == "rw") or (self.port_type == "r"): + self.copy_layout_pin(self.rbl_inst,"gnd") + self.copy_layout_pin(self.rbl_inst,"vdd") self.copy_layout_pin(self.ctrl_dff_inst,"gnd") self.copy_layout_pin(self.ctrl_dff_inst,"vdd") @@ -532,5 +786,71 @@ class control_logic(design.design): offset=pin.ll(), height=pin.height(), width=pin.width()) + + + def get_delays_to_wl(self): + """Get the delay (in delay units) of the clk to a wordline in the bitcell array""" + debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") + stage_efforts = self.determine_wordline_stage_efforts() + clk_to_wl_rise,clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(stage_efforts, self.parasitic_inv_delay) + total_delay = clk_to_wl_rise + clk_to_wl_fall + debug.info(1, "Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise, clk_to_wl_fall,total_delay)) + return clk_to_wl_rise,clk_to_wl_fall + + def determine_wordline_stage_efforts(self): + """Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts""" + stage_effort_list = [] + #Initial direction of gated_clk_bar signal for this path + is_clk_bar_rise = True + + #Calculate the load on wl_en within the module and add it to external load + external_cout = self.sram.get_wl_en_cin() + #First stage is the clock buffer + stage_effort_list += self.clkbuf.get_output_stage_efforts(external_cout, is_clk_bar_rise) + last_stage_is_rise = stage_effort_list[-1].is_rise + + #Then ask the sram for the other path delays (from the bank) + stage_effort_list += self.sram.determine_wordline_stage_efforts(last_stage_is_rise) + + return stage_effort_list + + def get_delays_to_sen(self): + """Get the delay (in delay units) of the clk to a sense amp enable. + This does not incorporate the delay of the replica bitline. + """ + debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") + stage_efforts = self.determine_sa_enable_stage_efforts() + clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(stage_efforts, self.parasitic_inv_delay) + total_delay = clk_to_sen_rise + clk_to_sen_fall + debug.info(1, "Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise, clk_to_sen_fall,total_delay)) + return clk_to_sen_rise, clk_to_sen_fall + + def determine_sa_enable_stage_efforts(self): + """Follows the gated_clk_bar signal to the sense amp enable signal adding each stages stage effort to a list""" + stage_effort_list = [] + #Calculate the load on clk_buf_bar + ext_clk_buf_cout = self.sram.get_clk_bar_cin() + + #Initial direction of clock signal for this path + last_stage_rise = True + + #First stage, gated_clk_bar -(and2)-> rbl_in. Only for RW ports. + if self.port_type == "rw": + stage1_cout = self.replica_bitline.get_en_cin() + stage_effort_list += self.and2.get_output_stage_efforts(stage1_cout, last_stage_rise) + last_stage_rise = stage_effort_list[-1].is_rise + + #Replica bitline stage, rbl_in -(rbl)-> pre_s_en + stage2_cout = self.buf8.get_cin() + stage_effort_list += self.replica_bitline.determine_sen_stage_efforts(stage2_cout, last_stage_rise) + last_stage_rise = stage_effort_list[-1].is_rise + + #buffer stage, pre_s_en -(buffer)-> s_en + stage3_cout = self.sram.get_sen_cin() + stage_effort_list += self.buf8.get_output_stage_efforts(stage3_cout, last_stage_rise) + last_stage_rise = stage_effort_list[-1].is_rise + + return stage_effort_list + \ No newline at end of file diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index 90175743..722328d1 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -13,8 +13,12 @@ class delay_chain(design.design): Usually, this will be constant, but it could have varied fanout. """ + unique_id = 1 + def __init__(self, fanout_list, name="delay_chain"): """init function""" + name = name+"_{}".format(delay_chain.unique_id) + delay_chain.unique_id += 1 design.design.__init__(self, name) # Two fanouts are needed so that we can route the vdd/gnd connections @@ -215,4 +219,23 @@ class delay_chain(design.design): start=mid_point, end=mid_point.scale(1,0)) + def get_cin(self): + """Get the enable input ralative capacitance""" + #Only 1 input to the delay chain which is connected to an inverter. + dc_cin = self.inv.get_cin() + return dc_cin + + def determine_delayed_en_stage_efforts(self, ext_delayed_en_cout, inp_is_rise=True): + """Get the stage efforts from the en to s_en. Does not compute the delay for the bitline load.""" + stage_effort_list = [] + #Add a stage to the list for every stage in delay chain. Stages only differ in fanout except the last which has an external cout. + last_stage_is_rise = inp_is_rise + for stage_fanout in self.fanout_list: + stage_cout = self.inv.get_cin()*(stage_fanout+1) + if len(stage_effort_list) == len(self.fanout_list)-1: #last stage + stage_cout+=ext_delayed_en_cout + stage = self.inv.get_effort_stage(stage_cout, last_stage_is_rise) + stage_effort_list.append(stage) + last_stage_is_rise = stage.is_rise + return stage_effort_list \ No newline at end of file diff --git a/compiler/modules/dff.py b/compiler/modules/dff.py index d72aae2e..19077689 100644 --- a/compiler/modules/dff.py +++ b/compiler/modules/dff.py @@ -2,7 +2,7 @@ import globals import design from math import log import design -from tech import GDS,layer +from tech import GDS,layer,spice,parameter import utils class dff(design.design): @@ -12,7 +12,7 @@ class dff(design.design): pin_names = ["D", "Q", "clk", "vdd", "gnd"] (width,height) = utils.get_libcell_size("dff", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"]) def __init__(self, name="dff"): design.design.__init__(self, name) @@ -23,7 +23,6 @@ class dff(design.design): def analytical_power(self, proc, vdd, temp, load): """Returns dynamic and leakage power. Results in nW""" - from tech import spice c_eff = self.calculate_effective_capacitance(load) f = spice["default_event_rate"] power_dyn = c_eff*vdd*vdd*f @@ -34,7 +33,7 @@ class dff(design.design): def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" - from tech import spice, parameter + from tech import parameter c_load = load c_para = spice["flop_para_cap"]#ff transition_prob = spice["flop_transition_prob"] @@ -42,7 +41,12 @@ class dff(design.design): def analytical_delay(self, slew, load = 0.0): # dont know how to calculate this now, use constant in tech file - from tech import spice result = self.return_delay(spice["dff_delay"], spice["dff_slew"]) return result + + def get_clk_cin(self): + """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" + #This is a handmade cell so the value must be entered in the tech.py file or estimated. + #Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width. + return parameter["dff_clk_cin"] diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index b1b1b361..25ef3090 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -59,7 +59,7 @@ class dff_array(design.design): self.dff_insts={} for row in range(self.rows): for col in range(self.columns): - name = "Xdff_r{0}_c{1}".format(row,col) + name = "dff_r{0}_c{1}".format(row,col) self.dff_insts[row,col]=self.add_inst(name=name, mod=self.dff) self.connect_inst([self.get_din_name(row,col), @@ -71,7 +71,7 @@ class dff_array(design.design): def place_dff_array(self): for row in range(self.rows): for col in range(self.columns): - name = "Xdff_r{0}_c{1}".format(row,col) + name = "dff_r{0}_c{1}".format(row,col) if (row % 2 == 0): base = vector(col*self.dff.width,row*self.dff.height) mirror = "R0" @@ -83,21 +83,21 @@ class dff_array(design.design): def get_din_name(self, row, col): if self.columns == 1: - din_name = "din[{0}]".format(row) + din_name = "din_{0}".format(row) elif self.rows == 1: - din_name = "din[{0}]".format(col) + din_name = "din_{0}".format(col) else: - din_name = "din[{0}][{1}]".format(row,col) + din_name = "din_{0}_{1}".format(row,col) return din_name def get_dout_name(self, row, col): if self.columns == 1: - dout_name = "dout[{0}]".format(row) + dout_name = "dout_{0}".format(row) elif self.rows == 1: - dout_name = "dout[{0}]".format(col) + dout_name = "dout_{0}".format(col) else: - dout_name = "dout[{0}][{1}]".format(row,col) + dout_name = "dout_{0}_{1}".format(row,col) return dout_name @@ -136,11 +136,12 @@ class dff_array(design.design): # Create vertical spines to a single horizontal rail clk_pin = self.dff_insts[0,0].get_pin("clk") + clk_ypos = 2*self.m3_pitch+self.m3_width debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2") self.add_layout_pin_segment_center(text="clk", layer="metal3", - start=vector(0,self.m3_pitch+self.m3_width), - end=vector(self.width,self.m3_pitch+self.m3_width)) + start=vector(0,clk_ypos), + end=vector(self.width,clk_ypos)) for col in range(self.columns): clk_pin = self.dff_insts[0,col].get_pin("clk") # Make a vertical strip for each column @@ -150,9 +151,15 @@ class dff_array(design.design): height=self.height) # Drop a via to the M3 pin self.add_via_center(layers=("metal2","via2","metal3"), - offset=vector(clk_pin.cx(),self.m3_pitch+self.m3_width)) + offset=vector(clk_pin.cx(),clk_ypos)) def analytical_delay(self, slew, load=0.0): return self.dff.analytical_delay(slew=slew, load=load) + + def get_clk_cin(self): + """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" + dff_clk_cin = self.dff.get_clk_cin() + total_cin = dff_clk_cin * self.rows * self.columns + return total_cin \ No newline at end of file diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 395216bf..9ff89603 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -1,6 +1,6 @@ import debug import design -from tech import drc +from tech import drc,parameter from math import log from vector import vector from globals import OPTS @@ -12,11 +12,13 @@ class dff_buf(design.design): with two inverters, of variable size, to provide q and qbar. This is to enable driving large fanout loads. """ - + unique_id = 1 + def __init__(self, inv1_size=2, inv2_size=4, name=""): if name=="": - name = "dff_buf_{0}_{1}".format(inv1_size, inv2_size) + name = "dff_buf_{0}".format(dff_buf.unique_id) + dff_buf.unique_id += 1 design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) @@ -36,13 +38,13 @@ class dff_buf(design.design): def create_netlist(self): self.add_modules() self.add_pins() - self.create_modules() + self.create_instances() def create_layout(self): self.width = self.dff.width + self.inv1.width + self.inv2.width self.height = self.dff.height - self.place_modules() + self.place_instances() self.route_wires() self.add_layout_pins() self.DRC_LVS() @@ -70,7 +72,7 @@ class dff_buf(design.design): self.add_pin("vdd") self.add_pin("gnd") - def create_modules(self): + def create_instances(self): self.dff_inst=self.add_inst(name="dff_buf_dff", mod=self.dff) self.connect_inst(["D", "qint", "clk", "vdd", "gnd"]) @@ -83,7 +85,7 @@ class dff_buf(design.design): mod=self.inv2) self.connect_inst(["Qb", "Q", "vdd", "gnd"]) - def place_modules(self): + def place_instances(self): # Add the DFF self.dff_inst.place(vector(0,0)) @@ -100,8 +102,7 @@ class dff_buf(design.design): mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx()) mid1 = vector(mid_x_offset, q_pin.cy()) mid2 = vector(mid_x_offset, a1_pin.cy()) - self.add_path("metal3", - [q_pin.center(), mid1, mid2, a1_pin.center()]) + self.add_path("metal3", [q_pin.center(), mid1, mid2, a1_pin.center()]) self.add_via_center(layers=("metal2","via2","metal3"), offset=q_pin.center()) self.add_via_center(layers=("metal2","via2","metal3"), @@ -112,8 +113,10 @@ class dff_buf(design.design): # Route inv1 z to inv2 a z1_pin = self.inv1_inst.get_pin("Z") a2_pin = self.inv2_inst.get_pin("A") - mid_point = vector(z1_pin.cx(), a2_pin.cy()) - self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()]) + mid_x_offset = 0.5*(z1_pin.cx() + a2_pin.cx()) + self.mid_qb_pos = vector(mid_x_offset, z1_pin.cy()) + mid2 = vector(mid_x_offset, a2_pin.cy()) + self.add_path("metal1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()]) def add_layout_pins(self): @@ -148,18 +151,22 @@ class dff_buf(design.design): height=din_pin.height()) dout_pin = self.inv2_inst.get_pin("Z") + mid_pos = dout_pin.center() + vector(self.m1_pitch,0) + q_pos = mid_pos - vector(0,self.m2_pitch) self.add_layout_pin_rect_center(text="Q", layer="metal2", - offset=dout_pin.center()) + offset=q_pos) + self.add_path("metal1", [dout_pin.center(), mid_pos, q_pos]) self.add_via_center(layers=("metal1","via1","metal2"), - offset=dout_pin.center()) + offset=q_pos) - dout_pin = self.inv2_inst.get_pin("A") + qb_pos = self.mid_qb_pos + vector(0,self.m2_pitch) self.add_layout_pin_rect_center(text="Qb", layer="metal2", - offset=dout_pin.center()) + offset=qb_pos) + self.add_path("metal1", [self.mid_qb_pos, qb_pos]) self.add_via_center(layers=("metal1","via1","metal2"), - offset=dout_pin.center()) + offset=qb_pos) @@ -170,3 +177,9 @@ class dff_buf(design.design): inv2_delay = self.inv2.analytical_delay(slew=inv1_delay.slew, load=load) return dff_delay + inv1_delay + inv2_delay + def get_clk_cin(self): + """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" + #This is a handmade cell so the value must be entered in the tech.py file or estimated. + #Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width. + #FIXME: Dff changed in a past commit. The parameter need to be updated. + return parameter["dff_clk_cin"] \ No newline at end of file diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index cedf0404..8097f207 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -11,13 +11,15 @@ class dff_buf_array(design.design): This is a simple row (or multiple rows) of flops. Unlike the data flops, these are never spaced out. """ - + unique_id = 1 + def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""): self.rows = rows self.columns = columns if name=="": - name = "dff_buf_array_{0}x{1}".format(rows, columns) + name = "dff_buf_array_{0}x{1}_{2}".format(rows, columns, dff_buf_array.unique_id) + dff_buf_array.unique_id += 1 design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) self.inv1_size = inv1_size @@ -59,7 +61,7 @@ class dff_buf_array(design.design): self.dff_insts={} for row in range(self.rows): for col in range(self.columns): - name = "Xdff_r{0}_c{1}".format(row,col) + name = "dff_r{0}_c{1}".format(row,col) self.dff_insts[row,col]=self.add_inst(name=name, mod=self.dff) self.connect_inst([self.get_din_name(row,col), @@ -84,31 +86,31 @@ class dff_buf_array(design.design): def get_din_name(self, row, col): if self.columns == 1: - din_name = "din[{0}]".format(row) + din_name = "din_{0}".format(row) elif self.rows == 1: - din_name = "din[{0}]".format(col) + din_name = "din_{0}".format(col) else: - din_name = "din[{0}][{1}]".format(row,col) + din_name = "din_{0}_{1}".format(row,col) return din_name def get_dout_name(self, row, col): if self.columns == 1: - dout_name = "dout[{0}]".format(row) + dout_name = "dout_{0}".format(row) elif self.rows == 1: - dout_name = "dout[{0}]".format(col) + dout_name = "dout_{0}".format(col) else: - dout_name = "dout[{0}][{1}]".format(row,col) + dout_name = "dout_{0}_{1}".format(row,col) return dout_name def get_dout_bar_name(self, row, col): if self.columns == 1: - dout_bar_name = "dout_bar[{0}]".format(row) + dout_bar_name = "dout_bar_{0}".format(row) elif self.rows == 1: - dout_bar_name = "dout_bar[{0}]".format(col) + dout_bar_name = "dout_bar_{0}".format(col) else: - dout_bar_name = "dout_bar[{0}][{1}]".format(row,col) + dout_bar_name = "dout_bar_{0}_{1}".format(row,col) return dout_bar_name @@ -153,6 +155,7 @@ class dff_buf_array(design.design): # Create vertical spines to a single horizontal rail clk_pin = self.dff_insts[0,0].get_pin("clk") + clk_ypos = 2*self.m3_pitch+self.m3_width debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2") if self.columns==1: self.add_layout_pin(text="clk", @@ -163,8 +166,8 @@ class dff_buf_array(design.design): else: self.add_layout_pin_segment_center(text="clk", layer="metal3", - start=vector(0,self.m3_pitch+self.m3_width), - end=vector(self.width,self.m3_pitch+self.m3_width)) + start=vector(0,clk_ypos), + end=vector(self.width,clk_ypos)) for col in range(self.columns): clk_pin = self.dff_insts[0,col].get_pin("clk") @@ -175,9 +178,15 @@ class dff_buf_array(design.design): height=self.height) # Drop a via to the M3 pin self.add_via_center(layers=("metal2","via2","metal3"), - offset=vector(clk_pin.cx(),self.m3_pitch+self.m3_width)) + offset=vector(clk_pin.cx(),clk_ypos)) def analytical_delay(self, slew, load=0.0): return self.dff.analytical_delay(slew=slew, load=load) + + def get_clk_cin(self): + """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" + dff_clk_cin = self.dff.get_clk_cin() + total_cin = dff_clk_cin * self.rows * self.columns + return total_cin \ No newline at end of file diff --git a/compiler/modules/dff_inv.py b/compiler/modules/dff_inv.py index 3a06c9c9..aebe49c2 100644 --- a/compiler/modules/dff_inv.py +++ b/compiler/modules/dff_inv.py @@ -11,11 +11,13 @@ class dff_inv(design.design): This is a simple DFF with an inverted output. Some DFFs do not have Qbar, so this will create it. """ - + unique_id = 1 + def __init__(self, inv_size=2, name=""): if name=="": - name = "dff_inv_{0}".format(inv_size) + name = "dff_inv_{0}".format(dff_inv.unique_id) + dff_inv.unique_id += 1 design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) self.inv_size = inv_size @@ -148,3 +150,6 @@ class dff_inv(design.design): inv1_delay = self.inv1.analytical_delay(slew=dff_delay.slew, load=load) return dff_delay + inv1_delay + def get_clk_cin(self): + """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" + return self.dff.get_clk_cin() \ No newline at end of file diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py index c2455821..8985b99a 100644 --- a/compiler/modules/dff_inv_array.py +++ b/compiler/modules/dff_inv_array.py @@ -11,13 +11,15 @@ class dff_inv_array(design.design): This is a simple row (or multiple rows) of flops. Unlike the data flops, these are never spaced out. """ - + unique_id = 1 + def __init__(self, rows, columns, inv_size=2, name=""): self.rows = rows self.columns = columns if name=="": - name = "dff_inv_array_{0}x{1}".format(rows, columns) + name = "dff_inv_array_{0}x{1}_{2}".format(rows, columns, dff_inv_array.unique_id) + dff_inv_array.unique_id += 1 design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) self.inv_size = inv_size @@ -84,31 +86,31 @@ class dff_inv_array(design.design): def get_din_name(self, row, col): if self.columns == 1: - din_name = "din[{0}]".format(row) + din_name = "din_{0}".format(row) elif self.rows == 1: - din_name = "din[{0}]".format(col) + din_name = "din_{0}".format(col) else: - din_name = "din[{0}][{1}]".format(row,col) + din_name = "din_{0}_{1}".format(row,col) return din_name def get_dout_name(self, row, col): if self.columns == 1: - dout_name = "dout[{0}]".format(row) + dout_name = "dout_{0}".format(row) elif self.rows == 1: - dout_name = "dout[{0}]".format(col) + dout_name = "dout_{0}".format(col) else: - dout_name = "dout[{0}][{1}]".format(row,col) + dout_name = "dout_{0}_{1}".format(row,col) return dout_name def get_dout_bar_name(self, row, col): if self.columns == 1: - dout_bar_name = "dout_bar[{0}]".format(row) + dout_bar_name = "dout_bar_{0}".format(row) elif self.rows == 1: - dout_bar_name = "dout_bar[{0}]".format(col) + dout_bar_name = "dout_bar_{0}".format(col) else: - dout_bar_name = "dout_bar[{0}][{1}]".format(row,col) + dout_bar_name = "dout_bar_{0}_{1}".format(row,col) return dout_bar_name @@ -153,6 +155,7 @@ class dff_inv_array(design.design): # Create vertical spines to a single horizontal rail clk_pin = self.dff_insts[0,0].get_pin("clk") + clk_ypos = 2*self.m3_pitch+self.m3_width debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2") if self.columns==1: self.add_layout_pin(text="clk", @@ -163,8 +166,8 @@ class dff_inv_array(design.design): else: self.add_layout_pin_segment_center(text="clk", layer="metal3", - start=vector(0,self.m3_pitch+self.m3_width), - end=vector(self.width,self.m3_pitch+self.m3_width)) + start=vector(0,clk_ypos), + end=vector(self.width,clk_ypos)) for col in range(self.columns): clk_pin = self.dff_insts[0,col].get_pin("clk") # Make a vertical strip for each column @@ -174,10 +177,16 @@ class dff_inv_array(design.design): height=self.height) # Drop a via to the M3 pin self.add_via_center(layers=("metal2","via2","metal3"), - offset=vector(clk_pin.cx(),self.m3_pitch+self.m3_width)) + offset=vector(clk_pin.cx(),clk_ypos)) def analytical_delay(self, slew, load=0.0): return self.dff.analytical_delay(slew=slew, load=load) + + def get_clk_cin(self): + """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" + dff_clk_cin = self.dff.get_clk_cin() + total_cin = dff_clk_cin * self.rows * self.columns + return total_cin \ No newline at end of file diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 0b44c8fc..32ed6d7c 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -17,22 +17,19 @@ class hierarchical_decoder(design.design): """ Dynamically generated hierarchical decoder. """ + unique_id = 1 + + def __init__(self, rows, height=None): + design.design.__init__(self, "hierarchical_decoder_{0}rows_{1}".format(rows,hierarchical_decoder.unique_id)) + hierarchical_decoder.unique_id += 1 - def __init__(self, rows): - design.design.__init__(self, "hierarchical_decoder_{0}rows".format(rows)) - - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - b = self.mod_bitcell() - self.bitcell_height = b.height - - self.NAND_FORMAT = "DEC_NAND[{0}]" - self.INV_FORMAT = "DEC_INV_[{0}]" + self.NAND_FORMAT = "DEC_NAND_{0}" + self.INV_FORMAT = "DEC_INV_{0}" self.pre2x4_inst = [] self.pre3x8_inst = [] + self.cell_height = height self.rows = rows self.num_inputs = int(math.log(self.rows, 2)) (self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) @@ -60,21 +57,21 @@ class hierarchical_decoder(design.design): self.DRC_LVS() def add_modules(self): - self.inv = pinv() + self.inv = pinv(height=self.cell_height) self.add_mod(self.inv) - self.nand2 = pnand2() + self.nand2 = pnand2(height=self.cell_height) self.add_mod(self.nand2) - self.nand3 = pnand3() + self.nand3 = pnand3(height=self.cell_height) self.add_mod(self.nand3) self.add_decoders() def add_decoders(self): """ Create the decoders based on the number of pre-decodes """ - self.pre2_4 = pre2x4() + self.pre2_4 = pre2x4(height=self.cell_height) self.add_mod(self.pre2_4) - self.pre3_8 = pre3x8() + self.pre3_8 = pre3x8(height=self.cell_height) self.add_mod(self.pre3_8) def determine_predecodes(self,num_inputs): @@ -168,7 +165,7 @@ class hierarchical_decoder(design.design): min_x = min(min_x, -self.pre3_8.width) input_offset=vector(min_x - self.input_routing_width,0) - input_bus_names = ["addr[{0}]".format(i) for i in range(self.num_inputs)] + input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)] self.input_rails = self.create_vertical_pin_bus(layer="metal2", pitch=self.m2_pitch, offset=input_offset, @@ -184,9 +181,9 @@ class hierarchical_decoder(design.design): for i in range(2): index = pre_num * 2 + i - input_pos = self.input_rails["addr[{}]".format(index)] + input_pos = self.input_rails["addr_{}".format(index)] - in_name = "in[{}]".format(i) + in_name = "in_{}".format(i) decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name) # To prevent conflicts, we will offset each input connect so @@ -201,9 +198,9 @@ class hierarchical_decoder(design.design): for i in range(3): index = pre_num * 3 + i + self.no_of_pre2x4 * 2 - input_pos = self.input_rails["addr[{}]".format(index)] + input_pos = self.input_rails["addr_{}".format(index)] - in_name = "in[{}]".format(i) + in_name = "in_{}".format(i) decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name) # To prevent conflicts, we will offset each input connect so @@ -230,10 +227,10 @@ class hierarchical_decoder(design.design): """ Add the module pins """ for i in range(self.num_inputs): - self.add_pin("addr[{0}]".format(i)) + self.add_pin("addr_{0}".format(i)) for j in range(self.rows): - self.add_pin("decode[{0}]".format(j)) + self.add_pin("decode_{0}".format(j)) self.add_pin("vdd") self.add_pin("gnd") @@ -258,12 +255,12 @@ class hierarchical_decoder(design.design): pins = [] for input_index in range(2): - pins.append("addr[{0}]".format(input_index + index_off1)) + pins.append("addr_{0}".format(input_index + index_off1)) for output_index in range(4): - pins.append("out[{0}]".format(output_index + index_off2)) + pins.append("out_{0}".format(output_index + index_off2)) pins.extend(["vdd", "gnd"]) - self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num), + self.pre2x4_inst.append(self.add_inst(name="pre_{0}".format(num), mod=self.pre2_4)) self.connect_inst(pins) @@ -277,12 +274,12 @@ class hierarchical_decoder(design.design): pins = [] for input_index in range(3): - pins.append("addr[{0}]".format(input_index + in_index_offset)) + pins.append("addr_{0}".format(input_index + in_index_offset)) for output_index in range(8): - pins.append("out[{0}]".format(output_index + out_index_offset)) + pins.append("out_{0}".format(output_index + out_index_offset)) pins.extend(["vdd", "gnd"]) - self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num), + self.pre3x8_inst.append(self.add_inst(name="pre3x8_{0}".format(num), mod=self.pre3_8)) self.connect_inst(pins) @@ -336,13 +333,13 @@ class hierarchical_decoder(design.design): if (self.num_inputs == 4 or self.num_inputs == 5): for i in range(len(self.predec_groups[0])): for j in range(len(self.predec_groups[1])): - row = len(self.predec_groups[1])*i + j + row = len(self.predec_groups[0])*j + i name = self.NAND_FORMAT.format(row) self.nand_inst.append(self.add_inst(name=name, mod=self.nand2)) - pins =["out[{0}]".format(i), - "out[{0}]".format(j + len(self.predec_groups[0])), - "Z[{0}]".format(row), + pins =["out_{0}".format(i), + "out_{0}".format(j + len(self.predec_groups[0])), + "Z_{0}".format(row), "vdd", "gnd"] self.connect_inst(pins) @@ -352,17 +349,17 @@ class hierarchical_decoder(design.design): for i in range(len(self.predec_groups[0])): for j in range(len(self.predec_groups[1])): for k in range(len(self.predec_groups[2])): - row = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \ - + len(self.predec_groups[2])*j + k + row = (len(self.predec_groups[0])*len(self.predec_groups[1])) * k \ + + len(self.predec_groups[0])*j + i name = self.NAND_FORMAT.format(row) self.nand_inst.append(self.add_inst(name=name, mod=self.nand3)) - pins = ["out[{0}]".format(i), - "out[{0}]".format(j + len(self.predec_groups[0])), - "out[{0}]".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])), - "Z[{0}]".format(row), + pins = ["out_{0}".format(i), + "out_{0}".format(j + len(self.predec_groups[0])), + "out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])), + "Z_{0}".format(row), "vdd", "gnd"] self.connect_inst(pins) @@ -377,8 +374,8 @@ class hierarchical_decoder(design.design): name = self.INV_FORMAT.format(row) self.inv_inst.append(self.add_inst(name=name, mod=self.inv)) - self.connect_inst(args=["Z[{0}]".format(row), - "decode[{0}]".format(row), + self.connect_inst(args=["Z_{0}".format(row), + "decode_{0}".format(row), "vdd", "gnd"]) @@ -466,7 +463,7 @@ class hierarchical_decoder(design.design): self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos]) z_pin = self.inv_inst[row].get_pin("Z") - self.add_layout_pin(text="decode[{0}]".format(row), + self.add_layout_pin(text="decode_{0}".format(row), layer="metal1", offset=z_pin.ll(), width=z_pin.width(), @@ -480,7 +477,7 @@ class hierarchical_decoder(design.design): # This is not needed for inputs <4 since they have no pre/decode stages. if (self.num_inputs >= 4): input_offset = vector(0.5*self.m2_width,0) - input_bus_names = ["predecode[{0}]".format(i) for i in range(self.total_number_of_predecoder_outputs)] + input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)] self.predecode_rails = self.create_vertical_pin_bus(layer="metal2", pitch=self.m2_pitch, offset=input_offset, @@ -497,8 +494,8 @@ class hierarchical_decoder(design.design): # FIXME: convert to connect_bus for pre_num in range(self.no_of_pre2x4): for i in range(4): - predecode_name = "predecode[{}]".format(pre_num * 4 + i) - out_name = "out[{}]".format(i) + predecode_name = "predecode_{}".format(pre_num * 4 + i) + out_name = "out_{}".format(i) pin = self.pre2x4_inst[pre_num].get_pin(out_name) self.route_predecode_rail_m3(predecode_name, pin) @@ -506,8 +503,8 @@ class hierarchical_decoder(design.design): # FIXME: convert to connect_bus for pre_num in range(self.no_of_pre3x8): for i in range(8): - predecode_name = "predecode[{}]".format(pre_num * 8 + i + self.no_of_pre2x4 * 4) - out_name = "out[{}]".format(i) + predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4) + out_name = "out_{}".format(i) pin = self.pre3x8_inst[pre_num].get_pin(out_name) self.route_predecode_rail_m3(predecode_name, pin) @@ -523,55 +520,52 @@ class hierarchical_decoder(design.design): """ row_index = 0 if (self.num_inputs == 4 or self.num_inputs == 5): - for index_A in self.predec_groups[0]: - for index_B in self.predec_groups[1]: + for index_B in self.predec_groups[1]: + for index_A in self.predec_groups[0]: # FIXME: convert to connect_bus? - predecode_name = "predecode[{}]".format(index_A) + predecode_name = "predecode_{}".format(index_A) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) - predecode_name = "predecode[{}]".format(index_B) + predecode_name = "predecode_{}".format(index_B) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) row_index = row_index + 1 elif (self.num_inputs > 5): - for index_A in self.predec_groups[0]: + for index_C in self.predec_groups[2]: for index_B in self.predec_groups[1]: - for index_C in self.predec_groups[2]: + for index_A in self.predec_groups[0]: # FIXME: convert to connect_bus? - predecode_name = "predecode[{}]".format(index_A) + predecode_name = "predecode_{}".format(index_A) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) - predecode_name = "predecode[{}]".format(index_B) + predecode_name = "predecode_{}".format(index_B) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) - predecode_name = "predecode[{}]".format(index_C) + predecode_name = "predecode_{}".format(index_C) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C")) row_index = row_index + 1 def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ - # Find the x offsets for where the vias/pins should be placed - a_xoffset = self.inv_inst[0].lx() - b_xoffset = self.inv_inst[0].rx() - + # The vias will be placed in the center and right of the cells, respectively. + xoffset = self.nand_inst[0].cx() for num in range(0,self.rows): - # this will result in duplicate polygons for rails, but who cares - - # Route both supplies - for n in ["vdd", "gnd"]: - supply_pin = self.inv_inst[num].get_pin(n) - - # Add pins in two locations - for xoffset in [a_xoffset, b_xoffset]: - pin_pos = vector(xoffset, supply_pin.cy()) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=pin_pos, - rotate=90) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=pin_pos, - rotate=90) - self.add_layout_pin_rect_center(text=n, - layer="metal3", - offset=pin_pos) + for pin_name in ["vdd", "gnd"]: + # The nand and inv are the same height rows... + supply_pin = self.nand_inst[num].get_pin(pin_name) + pin_pos = vector(xoffset, supply_pin.cy()) + self.add_power_pin(name=pin_name, + loc=pin_pos) + # Make a redundant rail too + for num in range(0,self.rows,2): + for pin_name in ["vdd", "gnd"]: + start = self.nand_inst[num].get_pin(pin_name).lc() + end = self.inv_inst[num].get_pin(pin_name).rc() + mid = (start+end).scale(0.5,0.5) + self.add_rect_center(layer="metal1", + offset=mid, + width=end.x-start.x) + + # Copy the pins from the predecoders for pre in self.pre2x4_inst + self.pre3x8_inst: self.copy_layout_pin(pre, "vdd") diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index cec3a925..7dfd443d 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -9,32 +9,31 @@ from globals import OPTS from pnand2 import pnand2 from pnand3 import pnand3 - class hierarchical_predecode(design.design): """ Pre 2x4 and 3x8 decoder shared code. """ - def __init__(self, input_number): + unique_id = 1 + + def __init__(self, input_number, height=None): self.number_of_inputs = input_number + self.cell_height = height self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) - design.design.__init__(self, name="pre{0}x{1}".format(self.number_of_inputs,self.number_of_outputs)) - - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) + design.design.__init__(self, name="pre{0}x{1}_{2}".format(self.number_of_inputs,self.number_of_outputs,hierarchical_predecode.unique_id)) + hierarchical_predecode.unique_id += 1 def add_pins(self): for k in range(self.number_of_inputs): - self.add_pin("in[{0}]".format(k)) + self.add_pin("in_{0}".format(k)) for i in range(self.number_of_outputs): - self.add_pin("out[{0}]".format(i)) + self.add_pin("out_{0}".format(i)) self.add_pin("vdd") self.add_pin("gnd") - def create_modules(self): - """ Create the INV and NAND gate """ + def add_modules(self): + """ Add the INV and NAND gate modules """ - self.inv = pinv() + self.inv = pinv(height=self.cell_height) self.add_mod(self.inv) self.add_nand(self.number_of_inputs) @@ -43,9 +42,9 @@ class hierarchical_predecode(design.design): def add_nand(self,inputs): """ Create the NAND for the predecode input stage """ if inputs==2: - self.nand = pnand2() + self.nand = pnand2(height=self.cell_height) elif inputs==3: - self.nand = pnand3() + self.nand = pnand3(height=self.cell_height) else: debug.error("Invalid number of predecode inputs: {}".format(inputs),-1) @@ -67,7 +66,7 @@ class hierarchical_predecode(design.design): def route_rails(self): """ Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """ - input_names = ["in[{}]".format(x) for x in range(self.number_of_inputs)] + input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)] offset = vector(0.5*self.m2_width,2*self.m1_width) self.input_rails = self.create_vertical_pin_bus(layer="metal2", pitch=self.m2_pitch, @@ -75,8 +74,8 @@ class hierarchical_predecode(design.design): names=input_names, length=self.height - 2*self.m1_width) - invert_names = ["Abar[{}]".format(x) for x in range(self.number_of_inputs)] - non_invert_names = ["A[{}]".format(x) for x in range(self.number_of_inputs)] + invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)] + non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)] decode_names = invert_names + non_invert_names offset = vector(self.x_off_inv_1 + self.inv.width + 2*self.m2_pitch, 2*self.m1_width) self.decode_rails = self.create_vertical_bus(layer="metal2", @@ -90,11 +89,11 @@ class hierarchical_predecode(design.design): """ Create the input inverters to invert input signals for the decode stage. """ self.in_inst = [] for inv_num in range(self.number_of_inputs): - name = "Xpre_inv[{0}]".format(inv_num) + name = "pre_inv_{0}".format(inv_num) self.in_inst.append(self.add_inst(name=name, mod=self.inv)) - self.connect_inst(["in[{0}]".format(inv_num), - "inbar[{0}]".format(inv_num), + self.connect_inst(["in_{0}".format(inv_num), + "inbar_{0}".format(inv_num), "vdd", "gnd"]) def place_input_inverters(self): @@ -114,11 +113,11 @@ class hierarchical_predecode(design.design): """ Create inverters for the inverted output decode signals. """ self.inv_inst = [] for inv_num in range(self.number_of_outputs): - name = "Xpre_nand_inv[{}]".format(inv_num) + name = "pre_nand_inv_{}".format(inv_num) self.inv_inst.append(self.add_inst(name=name, mod=self.inv)) - self.connect_inst(["Z[{}]".format(inv_num), - "out[{}]".format(inv_num), + self.connect_inst(["Z_{}".format(inv_num), + "out_{}".format(inv_num), "vdd", "gnd"]) @@ -140,7 +139,7 @@ class hierarchical_predecode(design.design): self.nand_inst = [] for nand_input in range(self.number_of_outputs): inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs) - name = "Xpre{0}_nand[{1}]".format(inout,nand_input) + name = "Xpre{0}_nand_{1}".format(inout,nand_input) self.nand_inst.append(self.add_inst(name=name, mod=self.nand)) self.connect_inst(connections[nand_input]) @@ -175,8 +174,8 @@ class hierarchical_predecode(design.design): # typically where the p/n devices are and there are no # pins in the nand gates. y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1m2.width + self.m1_space - in_pin = "in[{}]".format(num) - a_pin = "A[{}]".format(num) + in_pin = "in_{}".format(num) + a_pin = "A_{}".format(num) in_pos = vector(self.input_rails[in_pin].x,y_offset) a_pos = vector(self.decode_rails[a_pin].x,y_offset) self.add_path("metal1",[in_pos, a_pos]) @@ -202,7 +201,7 @@ class hierarchical_predecode(design.design): self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos]) z_pin = self.inv_inst[num].get_pin("Z") - self.add_layout_pin(text="out[{}]".format(num), + self.add_layout_pin(text="out_{}".format(num), layer="metal1", offset=z_pin.ll(), height=z_pin.height(), @@ -214,8 +213,8 @@ class hierarchical_predecode(design.design): Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd] """ for inv_num in range(self.number_of_inputs): - out_pin = "Abar[{}]".format(inv_num) - in_pin = "in[{}]".format(inv_num) + out_pin = "Abar_{}".format(inv_num) + in_pin = "in_{}".format(inv_num) #add output so that it is just below the vdd or gnd rail # since this is where the p/n devices are and there are no @@ -267,7 +266,7 @@ class hierarchical_predecode(design.design): # Find the x offsets for where the vias/pins should be placed in_xoffset = self.in_inst[0].rx() - out_xoffset = self.inv_inst[0].lx() + out_xoffset = self.inv_inst[0].lx() - self.m1_space for num in range(0,self.number_of_outputs): # this will result in duplicate polygons for rails, but who cares @@ -282,15 +281,7 @@ class hierarchical_predecode(design.design): # Add pins in two locations for xoffset in [in_xoffset, out_xoffset]: pin_pos = vector(xoffset, nand_pin.cy()) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=pin_pos, - rotate=90) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=pin_pos, - rotate=90) - self.add_layout_pin_rect_center(text=n, - layer="metal3", - offset=pin_pos) + self.add_power_pin(n, pin_pos) diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index 5a31cf9e..918172ea 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -9,8 +9,8 @@ class hierarchical_predecode2x4(hierarchical_predecode): """ Pre 2x4 decoder used in hierarchical_decoder. """ - def __init__(self): - hierarchical_predecode.__init__(self, 2) + def __init__(self, height=None): + hierarchical_predecode.__init__(self, 2, height) self.create_netlist() if not OPTS.netlist_only: @@ -18,13 +18,13 @@ class hierarchical_predecode2x4(hierarchical_predecode): def create_netlist(self): self.add_pins() - self.create_modules() + self.add_modules() self.create_input_inverters() self.create_output_inverters() - connections =[["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"], - ["in[0]", "inbar[1]", "Z[1]", "vdd", "gnd"], - ["inbar[0]", "in[1]", "Z[2]", "vdd", "gnd"], - ["in[0]", "in[1]", "Z[3]", "vdd", "gnd"]] + connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"], + ["in_0", "inbar_1", "Z_1", "vdd", "gnd"], + ["inbar_0", "in_1", "Z_2", "vdd", "gnd"], + ["in_0", "in_1", "Z_3", "vdd", "gnd"]] self.create_nand_array(connections) def create_layout(self): @@ -44,10 +44,10 @@ class hierarchical_predecode2x4(hierarchical_predecode): def get_nand_input_line_combination(self): """ These are the decoder connections of the NAND gates to the A,B pins """ - combination = [["Abar[0]", "Abar[1]"], - ["A[0]", "Abar[1]"], - ["Abar[0]", "A[1]"], - ["A[0]", "A[1]"]] + combination = [["Abar_0", "Abar_1"], + ["A_0", "Abar_1"], + ["Abar_0", "A_1"], + ["A_0", "A_1"]] return combination diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index e1b37ec0..88bbbcd7 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -9,8 +9,8 @@ class hierarchical_predecode3x8(hierarchical_predecode): """ Pre 3x8 decoder used in hierarchical_decoder. """ - def __init__(self): - hierarchical_predecode.__init__(self, 3) + def __init__(self, height=None): + hierarchical_predecode.__init__(self, 3, height) self.create_netlist() if not OPTS.netlist_only: @@ -18,17 +18,17 @@ class hierarchical_predecode3x8(hierarchical_predecode): def create_netlist(self): self.add_pins() - self.create_modules() + self.add_modules() self.create_input_inverters() self.create_output_inverters() - connections=[["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"], - ["in[0]", "inbar[1]", "inbar[2]", "Z[1]", "vdd", "gnd"], - ["inbar[0]", "in[1]", "inbar[2]", "Z[2]", "vdd", "gnd"], - ["in[0]", "in[1]", "inbar[2]", "Z[3]", "vdd", "gnd"], - ["inbar[0]", "inbar[1]", "in[2]", "Z[4]", "vdd", "gnd"], - ["in[0]", "inbar[1]", "in[2]", "Z[5]", "vdd", "gnd"], - ["inbar[0]", "in[1]", "in[2]", "Z[6]", "vdd", "gnd"], - ["in[0]", "in[1]", "in[2]", "Z[7]", "vdd", "gnd"]] + connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"], + ["in_0", "inbar_1", "inbar_2", "Z_1", "vdd", "gnd"], + ["inbar_0", "in_1", "inbar_2", "Z_2", "vdd", "gnd"], + ["in_0", "in_1", "inbar_2", "Z_3", "vdd", "gnd"], + ["inbar_0", "inbar_1", "in_2", "Z_4", "vdd", "gnd"], + ["in_0", "inbar_1", "in_2", "Z_5", "vdd", "gnd"], + ["inbar_0", "in_1", "in_2", "Z_6", "vdd", "gnd"], + ["in_0", "in_1", "in_2", "Z_7", "vdd", "gnd"]] self.create_nand_array(connections) def create_layout(self): @@ -49,14 +49,14 @@ class hierarchical_predecode3x8(hierarchical_predecode): def get_nand_input_line_combination(self): """ These are the decoder connections of the NAND gates to the A,B,C pins """ - combination = [["Abar[0]", "Abar[1]", "Abar[2]"], - ["A[0]", "Abar[1]", "Abar[2]"], - ["Abar[0]", "A[1]", "Abar[2]"], - ["A[0]", "A[1]", "Abar[2]"], - ["Abar[0]", "Abar[1]", "A[2]"], - ["A[0]", "Abar[1]", "A[2]"], - ["Abar[0]", "A[1]", "A[2]"], - ["A[0]", "A[1]", "A[2]"]] + combination = [["Abar_0", "Abar_1", "Abar_2"], + ["A_0", "Abar_1", "Abar_2"], + ["Abar_0", "A_1", "Abar_2"], + ["A_0", "A_1", "Abar_2"], + ["Abar_0", "Abar_1", "A_2"], + ["A_0", "Abar_1", "A_2"], + ["Abar_0", "A_1", "A_2"], + ["A_0", "A_1", "A_2"]] return combination diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py index e23fa6aa..d402b145 100644 --- a/compiler/modules/multibank.py +++ b/compiler/modules/multibank.py @@ -54,8 +54,8 @@ class multibank(design.design): self.compute_sizes() self.add_pins() - self.create_modules() self.add_modules() + self.create_instances() self.setup_layout_constraints() # FIXME: Move this to the add modules function @@ -75,11 +75,11 @@ class multibank(design.design): def add_pins(self): """ Adding pins for Bank module""" for i in range(self.word_size): - self.add_pin("DOUT[{0}]".format(i),"OUT") + self.add_pin("DOUT_{0}".format(i),"OUT") for i in range(self.word_size): - self.add_pin("BANK_DIN[{0}]".format(i),"IN") + self.add_pin("BANK_DIN_{0}".format(i),"IN") for i in range(self.addr_size): - self.add_pin("A[{0}]".format(i),"INPUT") + self.add_pin("A_{0}".format(i),"INPUT") # For more than one bank, we have a bank select and name # the signals gated_*. @@ -109,9 +109,9 @@ class multibank(design.design): if self.num_banks > 1: self.route_bank_select() - self.route_vdd_gnd() + self.route_supplies() - def add_modules(self): + def create_instances(self): """ Add modules. The order should not matter! """ # Above the bitcell array @@ -170,13 +170,13 @@ class multibank(design.design): self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width # A space for wells or jogging m2 - self.m2_gap = max(2*drc["pwell_to_nwell"] + drc["well_enclosure_active"], + self.m2_gap = max(2*drc("pwell_to_nwell"] + drc["well_enclosure_active"), 2*self.m2_pitch) - def create_modules(self): - """ Create all the modules using the class loader """ + def add_modules(self): + """ Add all the modules using the class loader """ self.tri = self.mod_tri_gate() self.bitcell = self.mod_bitcell() @@ -227,10 +227,10 @@ class multibank(design.design): offset=vector(0,0)) temp = [] for i in range(self.num_cols): - temp.append("bl[{0}]".format(i)) - temp.append("br[{0}]".format(i)) + temp.append("bl_{0}".format(i)) + temp.append("br_{0}".format(i)) for j in range(self.num_rows): - temp.append("wl[{0}]".format(j)) + temp.append("wl_{0}".format(j)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -246,8 +246,8 @@ class multibank(design.design): offset=vector(0,y_offset)) temp = [] for i in range(self.num_cols): - temp.append("bl[{0}]".format(i)) - temp.append("br[{0}]".format(i)) + temp.append("bl_{0}".format(i)) + temp.append("br_{0}".format(i)) temp.extend([self.prefix+"clk_buf_bar", "vdd"]) self.connect_inst(temp) @@ -265,13 +265,13 @@ class multibank(design.design): offset=vector(0,y_offset).scale(-1,-1)) temp = [] for i in range(self.num_cols): - temp.append("bl[{0}]".format(i)) - temp.append("br[{0}]".format(i)) + temp.append("bl_{0}".format(i)) + temp.append("br_{0}".format(i)) for k in range(self.words_per_row): - temp.append("sel[{0}]".format(k)) + temp.append("sel_{0}".format(k)) for j in range(self.word_size): - temp.append("bl_out[{0}]".format(j)) - temp.append("br_out[{0}]".format(j)) + temp.append("bl_out_{0}".format(j)) + temp.append("br_out_{0}".format(j)) temp.append("gnd") self.connect_inst(temp) @@ -284,13 +284,13 @@ class multibank(design.design): offset=vector(0,y_offset).scale(-1,-1)) temp = [] for i in range(self.word_size): - temp.append("sa_out[{0}]".format(i)) + temp.append("sa_out_{0}".format(i)) if self.words_per_row == 1: - temp.append("bl[{0}]".format(i)) - temp.append("br[{0}]".format(i)) + temp.append("bl_{0}".format(i)) + temp.append("br_{0}".format(i)) else: - temp.append("bl_out[{0}]".format(i)) - temp.append("br_out[{0}]".format(i)) + temp.append("bl_out_{0}".format(i)) + temp.append("br_out_{0}".format(i)) temp.extend([self.prefix+"s_en", "vdd", "gnd"]) self.connect_inst(temp) @@ -306,14 +306,14 @@ class multibank(design.design): temp = [] for i in range(self.word_size): - temp.append("BANK_DIN[{0}]".format(i)) + temp.append("BANK_DIN_{0}".format(i)) for i in range(self.word_size): if (self.words_per_row == 1): - temp.append("bl[{0}]".format(i)) - temp.append("br[{0}]".format(i)) + temp.append("bl_{0}".format(i)) + temp.append("br_{0}".format(i)) else: - temp.append("bl_out[{0}]".format(i)) - temp.append("br_out[{0}]".format(i)) + temp.append("bl_out_{0}".format(i)) + temp.append("br_out_{0}".format(i)) temp.extend([self.prefix+"w_en", "vdd", "gnd"]) self.connect_inst(temp) @@ -327,9 +327,9 @@ class multibank(design.design): temp = [] for i in range(self.word_size): - temp.append("sa_out[{0}]".format(i)) + temp.append("sa_out_{0}".format(i)) for i in range(self.word_size): - temp.append("DOUT[{0}]".format(i)) + temp.append("DOUT_{0}".format(i)) temp.extend([self.prefix+"tri_en", self.prefix+"tri_en_bar", "vdd", "gnd"]) self.connect_inst(temp) @@ -350,9 +350,9 @@ class multibank(design.design): temp = [] for i in range(self.row_addr_size): - temp.append("A[{0}]".format(i+self.col_addr_size)) + temp.append("A_{0}".format(i+self.col_addr_size)) for j in range(self.num_rows): - temp.append("dec_out[{0}]".format(j)) + temp.append("dec_out_{0}".format(j)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -367,9 +367,9 @@ class multibank(design.design): temp = [] for i in range(self.num_rows): - temp.append("dec_out[{0}]".format(i)) + temp.append("dec_out_{0}".format(i)) for i in range(self.num_rows): - temp.append("wl[{0}]".format(i)) + temp.append("wl_{0}".format(i)) temp.append(self.prefix+"clk_buf") temp.append("vdd") temp.append("gnd") @@ -382,16 +382,16 @@ class multibank(design.design): """ # Place the col decoder right aligned with row decoder x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) - y_off = -(self.col_decoder.height + 2*drc["well_to_well"]) + y_off = -(self.col_decoder.height + 2*drc("well_to_well")) self.col_decoder_inst=self.add_inst(name="col_address_decoder", mod=self.col_decoder, offset=vector(x_off,y_off)) temp = [] for i in range(self.col_addr_size): - temp.append("A[{0}]".format(i)) + temp.append("A_{0}".format(i)) for j in range(self.num_col_addr_lines): - temp.append("sel[{0}]".format(j)) + temp.append("sel_{0}".format(j)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -427,7 +427,7 @@ class multibank(design.design): y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by()) else: y_off = self.row_decoder_inst.by() - y_off -= (self.bank_select.height + drc["well_to_well"]) + y_off -= (self.bank_select.height + drc("well_to_well")) self.bank_select_pos = vector(x_off,y_off) self.bank_select_inst = self.add_inst(name="bank_select", mod=self.bank_select, @@ -440,33 +440,11 @@ class multibank(design.design): temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - def route_vdd_gnd(self): + def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ - - # These are the instances that every bank has - top_instances = [self.bitcell_array_inst, - self.precharge_array_inst, - self.sense_amp_array_inst, - self.write_driver_array_inst, -# self.tri_gate_array_inst, - self.row_decoder_inst, - self.wordline_driver_inst] - # Add these if we use the part... - if self.col_addr_size > 0: - top_instances.append(self.col_decoder_inst) - top_instances.append(self.col_mux_array_inst) - - if self.num_banks > 1: - top_instances.append(self.bank_select_inst) - - - for inst in top_instances: - # Column mux has no vdd - if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst): - self.copy_layout_pin(inst, "vdd") - # Precharge has no gnd - if inst != self.precharge_array_inst: - self.copy_layout_pin(inst, "gnd") + for inst in self.insts: + self.copy_power_pins(inst,"vdd") + self.copy_power_pins(inst,"gnd") def route_bank_select(self): """ Route the bank select logic. """ @@ -550,10 +528,10 @@ class multibank(design.design): """ Routing of BL and BR between pre-charge and bitcell array """ for i in range(self.num_cols): - precharge_bl = self.precharge_array_inst.get_pin("bl[{}]".format(i)).bc() - precharge_br = self.precharge_array_inst.get_pin("br[{}]".format(i)).bc() - bitcell_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).uc() - bitcell_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).uc() + precharge_bl = self.precharge_array_inst.get_pin("bl_{}".format(i)).bc() + precharge_br = self.precharge_array_inst.get_pin("br_{}".format(i)).bc() + bitcell_bl = self.bitcell_array_inst.get_pin("bl_{}".format(i)).uc() + bitcell_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).uc() yoffset = 0.5*(precharge_bl.y+bitcell_bl.y) self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset), @@ -570,10 +548,10 @@ class multibank(design.design): return for i in range(self.num_cols): - col_mux_bl = self.col_mux_array_inst.get_pin("bl[{}]".format(i)).uc() - col_mux_br = self.col_mux_array_inst.get_pin("br[{}]".format(i)).uc() - bitcell_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).bc() - bitcell_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).bc() + col_mux_bl = self.col_mux_array_inst.get_pin("bl_{}".format(i)).uc() + col_mux_br = self.col_mux_array_inst.get_pin("br_{}".format(i)).uc() + bitcell_bl = self.bitcell_array_inst.get_pin("bl_{}".format(i)).bc() + bitcell_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).bc() yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y) self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset), @@ -585,17 +563,17 @@ class multibank(design.design): """ Routing of BL and BR between sense_amp and column mux or bitcell array """ for i in range(self.word_size): - sense_amp_bl = self.sense_amp_array_inst.get_pin("bl[{}]".format(i)).uc() - sense_amp_br = self.sense_amp_array_inst.get_pin("br[{}]".format(i)).uc() + sense_amp_bl = self.sense_amp_array_inst.get_pin("bl_{}".format(i)).uc() + sense_amp_br = self.sense_amp_array_inst.get_pin("br_{}".format(i)).uc() if self.col_addr_size>0: # Sense amp is connected to the col mux - connect_bl = self.col_mux_array_inst.get_pin("bl_out[{}]".format(i)).bc() - connect_br = self.col_mux_array_inst.get_pin("br_out[{}]".format(i)).bc() + connect_bl = self.col_mux_array_inst.get_pin("bl_out_{}".format(i)).bc() + connect_br = self.col_mux_array_inst.get_pin("br_out_{}".format(i)).bc() else: # Sense amp is directly connected to the bitcell array - connect_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).bc() - connect_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).bc() + connect_bl = self.bitcell_array_inst.get_pin("bl_{}".format(i)).bc() + connect_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).bc() yoffset = 0.5*(sense_amp_bl.y+connect_bl.y) @@ -609,8 +587,8 @@ class multibank(design.design): for i in range(self.word_size): # Connection of data_out of sense amp to data_in - tri_gate_in = self.tri_gate_array_inst.get_pin("in[{}]".format(i)).lc() - sa_data_out = self.sense_amp_array_inst.get_pin("data[{}]".format(i)).bc() + tri_gate_in = self.tri_gate_array_inst.get_pin("in_{}".format(i)).lc() + sa_data_out = self.sense_amp_array_inst.get_pin("data_{}".format(i)).bc() self.add_via_center(layers=("metal2", "via2", "metal3"), offset=tri_gate_in) @@ -621,8 +599,8 @@ class multibank(design.design): def route_sense_amp_out(self): """ Add pins for the sense amp output """ for i in range(self.word_size): - data_pin = self.sense_amp_array_inst.get_pin("data[{}]".format(i)) - self.add_layout_pin_rect_center(text="DOUT[{}]".format(i), + data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(i)) + self.add_layout_pin_rect_center(text="DOUT_{}".format(i), layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), @@ -631,8 +609,8 @@ class multibank(design.design): def route_tri_gate_out(self): """ Metal 3 routing of tri_gate output data """ for i in range(self.word_size): - data_pin = self.tri_gate_array_inst.get_pin("out[{}]".format(i)) - self.add_layout_pin_rect_center(text="DOUT[{}]".format(i), + data_pin = self.tri_gate_array_inst.get_pin("out_{}".format(i)) + self.add_layout_pin_rect_center(text="DOUT_{}".format(i), layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), @@ -645,8 +623,8 @@ class multibank(design.design): # Create inputs for the row address lines for i in range(self.row_addr_size): addr_idx = i + self.col_addr_size - decoder_name = "A[{}]".format(i) - addr_name = "A[{}]".format(addr_idx) + decoder_name = "A_{}".format(i) + addr_name = "A_{}".format(addr_idx) self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name) @@ -654,8 +632,8 @@ class multibank(design.design): """ Connecting write driver """ for i in range(self.word_size): - data_name = "data[{}]".format(i) - din_name = "BANK_DIN[{}]".format(i) + data_name = "data_{}".format(i) + din_name = "BANK_DIN_{}".format(i) self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) @@ -666,15 +644,15 @@ class multibank(design.design): # we don't care about bends after connecting to the input pin, so let the path code decide. for i in range(self.num_rows): # The pre/post is to access the pin from "outside" the cell to avoid DRCs - decoder_out_pos = self.row_decoder_inst.get_pin("decode[{}]".format(i)).rc() - driver_in_pos = self.wordline_driver_inst.get_pin("in[{}]".format(i)).lc() + decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(i)).rc() + driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(i)).lc() mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos]) # The mid guarantees we exit the input cell to the right. - driver_wl_pos = self.wordline_driver_inst.get_pin("wl[{}]".format(i)).rc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin("wl[{}]".format(i)).lc() + driver_wl_pos = self.wordline_driver_inst.get_pin("wl_{}".format(i)).rc() + bitcell_wl_pos = self.bitcell_array_inst.get_pin("wl_{}".format(i)).lc() mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0) mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1) self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) @@ -699,20 +677,20 @@ class multibank(design.design): elif self.col_addr_size > 1: decode_names = [] for i in range(self.num_col_addr_lines): - decode_names.append("out[{}]".format(i)) + decode_names.append("out_{}".format(i)) for i in range(self.col_addr_size): - decoder_name = "in[{}]".format(i) - addr_name = "A[{}]".format(i) + decoder_name = "in_{}".format(i) + addr_name = "A_{}".format(i) self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name) # This will do a quick "river route" on two layers. # When above the top select line it will offset "inward" again to prevent conflicts. # This could be done on a single layer, but we follow preferred direction rules for later routing. - top_y_offset = self.col_mux_array_inst.get_pin("sel[{}]".format(self.num_col_addr_lines-1)).cy() + top_y_offset = self.col_mux_array_inst.get_pin("sel_{}".format(self.num_col_addr_lines-1)).cy() for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)): - mux_name = "sel[{}]".format(i) + mux_name = "sel_{}".format(i) mux_addr_pos = self.col_mux_array_inst.get_pin(mux_name).lc() decode_out_pos = self.col_decoder_inst.get_pin(decode_name).center() @@ -738,7 +716,7 @@ class multibank(design.design): """ # Add the wordline names for i in range(self.num_rows): - wl_name = "wl[{}]".format(i) + wl_name = "wl_{}".format(i) wl_pin = self.bitcell_array_inst.get_pin(wl_name) self.add_label(text=wl_name, layer="metal1", @@ -746,8 +724,8 @@ class multibank(design.design): # Add the bitline names for i in range(self.num_cols): - bl_name = "bl[{}]".format(i) - br_name = "br[{}]".format(i) + bl_name = "bl_{}".format(i) + br_name = "br_{}".format(i) bl_pin = self.bitcell_array_inst.get_pin(bl_name) br_pin = self.bitcell_array_inst.get_pin(br_name) self.add_label(text=bl_name, @@ -759,16 +737,16 @@ class multibank(design.design): # # Add the data output names to the sense amp output # for i in range(self.word_size): - # data_name = "data[{}]".format(i) + # data_name = "data_{}".format(i) # data_pin = self.sense_amp_array_inst.get_pin(data_name) - # self.add_label(text="sa_out[{}]".format(i), + # self.add_label(text="sa_out_{}".format(i), # layer="metal2", # offset=data_pin.center()) # Add labels on the decoder for i in range(self.word_size): - data_name = "dec_out[{}]".format(i) - pin_name = "in[{}]".format(i) + data_name = "dec_out_{}".format(i) + pin_name = "in_{}".format(i) data_pin = self.wordline_driver_inst.get_pin(pin_name) self.add_label(text=data_name, layer="metal1", diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 880288c6..8bb66cda 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -31,9 +31,9 @@ class precharge_array(design.design): def add_pins(self): """Adds pins for spice file""" for i in range(self.columns): - self.add_pin("bl[{0}]".format(i)) - self.add_pin("br[{0}]".format(i)) - self.add_pin("en") + self.add_pin("bl_{0}".format(i)) + self.add_pin("br_{0}".format(i)) + self.add_pin("en_bar") self.add_pin("vdd") def create_netlist(self): @@ -59,11 +59,11 @@ class precharge_array(design.design): def add_layout_pins(self): - self.add_layout_pin(text="en", + self.add_layout_pin(text="en_bar", layer="metal1", - offset=self.pc_cell.get_pin("en").ll(), + offset=self.pc_cell.get_pin("en_bar").ll(), width=self.width, - height=drc["minwidth_metal1"]) + height=drc("minwidth_metal1")) for inst in self.local_insts: self.copy_layout_pin(inst, "vdd") @@ -71,16 +71,16 @@ class precharge_array(design.design): for i in range(len(self.local_insts)): inst = self.local_insts[i] bl_pin = inst.get_pin("bl") - self.add_layout_pin(text="bl[{0}]".format(i), + self.add_layout_pin(text="bl_{0}".format(i), layer="metal2", offset=bl_pin.ll(), - width=drc["minwidth_metal2"], + width=drc("minwidth_metal2"), height=bl_pin.height()) br_pin = inst.get_pin("br") - self.add_layout_pin(text="br[{0}]".format(i), + self.add_layout_pin(text="br_{0}".format(i), layer="metal2", offset=br_pin.ll(), - width=drc["minwidth_metal2"], + width=drc("minwidth_metal2"), height=bl_pin.height()) @@ -94,7 +94,7 @@ class precharge_array(design.design): mod=self.pc_cell, offset=offset) self.local_insts.append(inst) - self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i), "en", "vdd"]) + self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en_bar", "vdd"]) def place_insts(self): @@ -102,3 +102,9 @@ class precharge_array(design.design): for i in range(self.columns): offset = vector(self.pc_cell.width * i, 0) self.local_insts[i].place(offset) + + def get_en_cin(self): + """Get the relative capacitance of all the clk connections in the precharge array""" + #Assume single port + precharge_en_cin = self.pc_cell.get_en_cin() + return precharge_en_cin*self.columns \ No newline at end of file diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index 53430ac6..8538fbee 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -15,12 +15,11 @@ class replica_bitline(design.design): line and rows is the height of the replica bit loads. """ - def __init__(self, delay_stages, delay_fanout, bitcell_loads, name="replica_bitline"): + def __init__(self, delay_fanout_list, bitcell_loads, name="replica_bitline"): design.design.__init__(self, name) self.bitcell_loads = bitcell_loads - self.delay_stages = delay_stages - self.delay_fanout = delay_fanout + self.delay_fanout_list = delay_fanout_list self.create_netlist() if not OPTS.netlist_only: @@ -29,11 +28,11 @@ class replica_bitline(design.design): def create_netlist(self): self.add_modules() self.add_pins() - self.create_modules() + self.create_instances() def create_layout(self): self.calculate_module_offsets() - self.place_modules() + self.place_instances() self.route() self.add_layout_pins() @@ -76,13 +75,12 @@ class replica_bitline(design.design): self.access_tx_offset = vector(-gap_width-self.access_tx.width-self.inv.width, 0.5*self.inv.height) - def add_modules(self): """ Add the modules for later usage """ from importlib import reload - g = reload(__import__(OPTS.delay_chain)) - self.mod_delay_chain = getattr(g, OPTS.delay_chain) + #g = reload(__import__(OPTS.delay_chain)) + #self.mod_delay_chain = getattr(g, OPTS.delay_chain) g = reload(__import__(OPTS.replica_bitcell)) self.mod_replica_bitcell = getattr(g, OPTS.replica_bitcell) @@ -91,11 +89,12 @@ class replica_bitline(design.design): self.add_mod(self.bitcell) # This is the replica bitline load column that is the height of our array - self.rbl = bitcell_array(name="bitline_load", cols=1, rows=self.bitcell_loads) + self.rbl = bitcell_array(cols=1, rows=self.bitcell_loads) self.add_mod(self.rbl) # FIXME: The FO and depth of this should be tuned - self.delay_chain = self.mod_delay_chain([self.delay_fanout]*self.delay_stages) + from delay_chain import delay_chain + self.delay_chain = delay_chain(self.delay_fanout_list) self.add_mod(self.delay_chain) self.inv = pinv() @@ -104,18 +103,18 @@ class replica_bitline(design.design): self.access_tx = ptx(tx_type="pmos") self.add_mod(self.access_tx) - def create_modules(self): + def create_instances(self): """ Create all of the module instances in the logical netlist """ # This is the threshold detect inverter on the output of the RBL self.rbl_inv_inst=self.add_inst(name="rbl_inv", mod=self.inv) - self.connect_inst(["bl[0]", "out", "vdd", "gnd"]) + self.connect_inst(["bl0_0", "out", "vdd", "gnd"]) self.tx_inst=self.add_inst(name="rbl_access_tx", mod=self.access_tx) # D, G, S, B - self.connect_inst(["vdd", "delayed_en", "bl[0]", "vdd"]) + self.connect_inst(["vdd", "delayed_en", "bl0_0", "vdd"]) # add the well and poly contact self.dc_inst=self.add_inst(name="delay_chain", @@ -124,28 +123,35 @@ class replica_bitline(design.design): self.rbc_inst=self.add_inst(name="bitcell", mod=self.replica_bitcell) - self.connect_inst(["bl[0]", "br[0]", "delayed_en", "vdd", "gnd"]) + temp = [] + for port in self.all_ports: + temp.append("bl{}_0".format(port)) + temp.append("br{}_0".format(port)) + for port in self.all_ports: + temp.append("delayed_en") + temp.append("vdd") + temp.append("gnd") + self.connect_inst(temp) + #self.connect_inst(["bl_0", "br_0", "delayed_en", "vdd", "gnd"]) self.rbl_inst=self.add_inst(name="load", mod=self.rbl) - total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports temp = [] - temp.append("bl[0]") - temp.append("br[0]") - for port in range(total_ports - 1): - temp.append("gnd") - temp.append("gnd") + for port in self.all_ports: + temp.append("bl{}_0".format(port)) + temp.append("br{}_0".format(port)) for wl in range(self.bitcell_loads): - for port in range(total_ports): + for port in self.all_ports: temp.append("gnd") temp.append("vdd") temp.append("gnd") self.connect_inst(temp) self.wl_list = self.rbl.cell.list_all_wl_names() + self.bl_list = self.rbl.cell.list_all_bl_names() - def place_modules(self): + def place_instances(self): """ Add all of the module instances in the logical netlist """ # This is the threshold detect inverter on the output of the RBL @@ -160,9 +166,6 @@ class replica_bitline(design.design): mirror="MX") self.rbl_inst.place(self.rbl_offset) - - - def route(self): @@ -175,19 +178,76 @@ class replica_bitline(design.design): """ Connect the RBL word lines to gnd """ # Connect the WL and gnd pins directly to the center and right gnd rails for row in range(self.bitcell_loads): - wl = self.wl_list[0]+"[{}]".format(row) + wl = self.wl_list[0]+"_{}".format(row) pin = self.rbl_inst.get_pin(wl) - # Route the connection to the right so that it doesn't interfere - # with the cells + # Route the connection to the right so that it doesn't interfere with the cells + # Wordlines may be close to each other when tiled, so gnd connections are routed in opposite directions pin_right = pin.rc() - pin_extension = pin_right + vector(self.m1_pitch,0) + pin_extension = pin_right + vector(self.m3_pitch,0) + if pin.layer != "metal1": continue - self.add_path("metal1", [pin_right, pin_extension]) + pin_width_ydir = pin.uy()-pin.by() + #Width is set to pin y width to avoid DRC issues with m1 gaps + self.add_path("metal1", [pin_right, pin_extension], pin_width_ydir) self.add_power_pin("gnd", pin_extension) - - + + # for multiport, need to short wordlines to each other so they all connect to gnd. + wl_last = self.wl_list[-1]+"_{}".format(row) + pin_last = self.rbl_inst.get_pin(wl_last) + self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0)) + + def short_wordlines(self, wl_pin_a, wl_pin_b, pin_side, is_replica_cell, cell_row=0, offset_x_vec=None): + """Connects the word lines together for a single bitcell. Also requires which side of the bitcell to short the pins.""" + #Assumes input pins are wordlines. Also assumes the word lines are horizontal in metal1. Also assumes pins have same x coord. + #This is my (Hunter) first time editing layout in openram so this function is likely not optimal. + if len(self.all_ports) > 1: + #1. Create vertical metal for all the bitlines to connect to + #m1 needs to be extended in the y directions, direction needs to be determined as every other cell is flipped + correct_y = vector(0, 0.5*drc("minwidth_metal1")) + #x spacing depends on the side being drawn. Unknown to me (Hunter) why the size of the space differs by the side. + #I assume this is related to how a wire is draw, but I have not investigated the issue. + if pin_side == "right": + correct_x = vector(0.5*drc("minwidth_metal1"), 0) + if offset_x_vec != None: + correct_x = offset_x_vec + else: + correct_x = vector(1.5*drc("minwidth_metal1"), 0) + + if wl_pin_a.uy() > wl_pin_b.uy(): + self.add_path("metal1", [wl_pin_a.rc()+correct_x+correct_y, wl_pin_b.rc()+correct_x-correct_y]) + else: + self.add_path("metal1", [wl_pin_a.rc()+correct_x-correct_y, wl_pin_b.rc()+correct_x+correct_y]) + elif pin_side == "left": + if offset_x_vec != None: + correct_x = offset_x_vec + else: + correct_x = vector(1.5*drc("minwidth_metal1"), 0) + + if wl_pin_a.uy() > wl_pin_b.uy(): + self.add_path("metal1", [wl_pin_a.lc()-correct_x+correct_y, wl_pin_b.lc()-correct_x-correct_y]) + else: + self.add_path("metal1", [wl_pin_a.lc()-correct_x-correct_y, wl_pin_b.lc()-correct_x+correct_y]) + else: + debug.error("Could not connect wordlines on specified input side={}".format(pin_side),1) + + #2. Connect word lines horizontally. Only replica cell needs. Bitline loads currently already do this. + for port in self.all_ports: + if is_replica_cell: + wl = self.wl_list[port] + pin = self.rbc_inst.get_pin(wl) + else: + wl = self.wl_list[port]+"_{}".format(cell_row) + pin = self.rbl_inst.get_pin(wl) + + if pin_side == "left": + self.add_path("metal1", [pin.lc()-correct_x, pin.lc()]) + elif pin_side == "right": + self.add_path("metal1", [pin.rc()+correct_x, pin.rc()]) + + + def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ @@ -204,10 +264,8 @@ class replica_bitline(design.design): pin = self.rbl_inv_inst.get_pin("vdd") self.add_power_pin("vdd", pin.lc()) - # Replica bitcell needs to be routed up to M3 pin=self.rbc_inst.get_pin("vdd") - # Don't rotate this via to vit in FreePDK45 - self.add_power_pin("vdd", pin.center(), rotate=0) + self.add_power_pin("vdd", pin.center(), 0, pin.layer) for pin in self.rbc_inst.get_pins("gnd"): self.add_power_pin("gnd", pin.center()) @@ -243,16 +301,28 @@ class replica_bitline(design.design): # 3. Route the contact of previous route to the bitcell WL # route bend of previous net to bitcell WL - wl_offset = self.rbc_inst.get_pin("wl").lc() - xmid_point= 0.5*(wl_offset.x+contact_offset.x) - wl_mid1 = vector(xmid_point,contact_offset.y) - wl_mid2 = vector(xmid_point,wl_offset.y) - self.add_path("metal1", [contact_offset, wl_mid1, wl_mid2, wl_offset]) + wl_offset = self.rbc_inst.get_pin(self.wl_list[0]).lc() + wl_mid1 = wl_offset - vector(1.5*drc("minwidth_metal1"), 0) + wl_mid2 = vector(wl_mid1.x, contact_offset.y) + #xmid_point= 0.5*(wl_offset.x+contact_offset.x) + #wl_mid1 = vector(xmid_point,contact_offset.y) + #wl_mid2 = vector(xmid_point,wl_offset.y) + self.add_path("metal1", [wl_offset, wl_mid1, wl_mid2, contact_offset]) + + # 4. Short wodlines if multiport + wl = self.wl_list[0] + wl_last = self.wl_list[-1] + pin = self.rbc_inst.get_pin(wl) + pin_last = self.rbc_inst.get_pin(wl_last) + x_offset = self.short_wordlines(pin, pin_last, "left", True) + + #correct = vector(0.5*drc("minwidth_metal1"), 0) + #self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct]) # DRAIN ROUTE # Route the drain to the vdd rail drain_offset = self.tx_inst.get_pin("D").center() - self.add_power_pin("vdd", drain_offset) + self.add_power_pin("vdd", drain_offset, rotate=0) # SOURCE ROUTE # Route the drain to the RBL inverter input @@ -262,7 +332,7 @@ class replica_bitline(design.design): # Route the connection of the source route to the RBL bitline (left) # Via will go halfway down from the bitcell - bl_offset = self.rbc_inst.get_pin("bl").bc() + bl_offset = self.rbc_inst.get_pin(self.bl_list[0]).bc() # Route down a pitch so we can use M2 routing bl_down_offset = bl_offset - vector(0, self.m2_pitch) self.add_path("metal2",[source_offset, bl_down_offset, bl_offset]) @@ -386,7 +456,7 @@ class replica_bitline(design.design): # Connect the WL and gnd pins directly to the center and right gnd rails for row in range(self.bitcell_loads): - wl = self.wl_list[0]+"[{}]".format(row) + wl = self.wl_list[0]+"_{}".format(row) pin = self.rbl_inst.get_pin(wl) if pin.layer != "metal1": continue @@ -530,4 +600,35 @@ class replica_bitline(design.design): offset=pin.ll(), height=pin.height(), width=pin.width()) - + + def get_en_cin(self): + """Get the enable input relative capacitance""" + #The enable is only connected to the delay, get the cin from that module + en_cin = self.delay_chain.get_cin() + return en_cin + + def determine_sen_stage_efforts(self, ext_cout, inp_is_rise=True): + """Get the stage efforts from the en to s_en. Does not compute the delay for the bitline load.""" + stage_effort_list = [] + #Stage 1 is the delay chain + stage1_cout = self.get_delayed_en_cin() + stage1 = self.delay_chain.determine_delayed_en_stage_efforts(stage1_cout, inp_is_rise) + stage_effort_list += stage1 + + #There is a disconnect between the delay chain and inverter. The rise/fall of the input to the inverter + #Will be the negation of the previous stage. + last_stage_is_rise = not stage_effort_list[-1].is_rise + + #The delay chain triggers the enable on the replica bitline (rbl). This is used to track the bitline delay whereas this + #model is intended to track every but that. Therefore, the next stage is the inverter after the rbl. + stage2 = self.inv.get_effort_stage(ext_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_delayed_en_cin(self): + """Get the fanout capacitance (relative) of the delayed enable from the delay chain.""" + access_tx_cin = self.access_tx.get_cin() + rbc_cin = self.replica_bitcell.get_wl_cin() + return access_tx_cin + rbc_cin + \ No newline at end of file diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index 45a195fc..8553e42c 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -1,7 +1,7 @@ import design import debug import utils -from tech import GDS,layer +from tech import GDS,layer, parameter,drc class sense_amp(design.design): """ @@ -13,7 +13,7 @@ class sense_amp(design.design): pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"] (width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"]) def __init__(self, name): design.design.__init__(self, name) @@ -23,6 +23,14 @@ class sense_amp(design.design): self.height = sense_amp.height self.pin_map = sense_amp.pin_map + def input_load(self): + #Input load for the bitlines which are connected to the source/drain of a TX. Not the selects. + from tech import spice, parameter + # Default is 8x. Per Samira and Hodges-Jackson book: + # "Column-mux transistors driven by the decoder must be sized for optimal speed" + bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file. + return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff + def analytical_delay(self, slew, load=0.0): from tech import spice r = spice["min_tx_r"]/(10) @@ -35,3 +43,9 @@ class sense_amp(design.design): #Power in this module currently not defined. Returns 0 nW (leakage and dynamic). total_power = self.return_power() return total_power + + def get_en_cin(self): + """Get the relative capacitance of sense amp enable gate cin""" + pmos_cin = parameter["sa_en_pmos_size"]/drc("minwidth_tx") + nmos_cin = parameter["sa_en_nmos_size"]/drc("minwidth_tx") + return 2*pmos_cin + nmos_cin \ No newline at end of file diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index c48d280d..806acf71 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -43,9 +43,9 @@ class sense_amp_array(design.design): def add_pins(self): for i in range(0,self.word_size): - self.add_pin("data[{0}]".format(i)) - self.add_pin("bl[{0}]".format(i)) - self.add_pin("br[{0}]".format(i)) + self.add_pin("data_{0}".format(i)) + self.add_pin("bl_{0}".format(i)) + self.add_pin("br_{0}".format(i)) self.add_pin("en") self.add_pin("vdd") self.add_pin("gnd") @@ -70,9 +70,9 @@ class sense_amp_array(design.design): name = "sa_d{0}".format(i) self.local_insts.append(self.add_inst(name=name, mod=self.amp)) - self.connect_inst(["bl[{0}]".format(i), - "br[{0}]".format(i), - "data[{0}]".format(i), + self.connect_inst(["bl_{0}".format(i), + "br_{0}".format(i), + "data_{0}".format(i), "en", "vdd", "gnd"]) def place_sense_amp_array(self): @@ -107,18 +107,18 @@ class sense_amp_array(design.design): br_pin = inst.get_pin("br") dout_pin = inst.get_pin("dout") - self.add_layout_pin(text="bl[{0}]".format(i), + self.add_layout_pin(text="bl_{0}".format(i), layer="metal2", offset=bl_pin.ll(), width=bl_pin.width(), height=bl_pin.height()) - self.add_layout_pin(text="br[{0}]".format(i), + self.add_layout_pin(text="br_{0}".format(i), layer="metal2", offset=br_pin.ll(), width=br_pin.width(), height=br_pin.height()) - self.add_layout_pin(text="data[{0}]".format(i), + self.add_layout_pin(text="data_{0}".format(i), layer="metal2", offset=dout_pin.ll(), width=dout_pin.width(), @@ -132,8 +132,15 @@ class sense_amp_array(design.design): layer="metal1", offset=sclk_offset, width=self.width, - height=drc["minwidth_metal1"]) + height=drc("minwidth_metal1")) + def input_load(self): + return self.amp.input_load() + def analytical_delay(self, slew, load=0.0): return self.amp.analytical_delay(slew=slew, load=load) + def get_en_cin(self): + """Get the relative capacitance of all the sense amp enable connections in the array""" + sense_amp_en_cin = self.amp.get_en_cin() + return sense_amp_en_cin * self.words_per_row \ No newline at end of file diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index e7ef1166..699e1d85 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -50,19 +50,18 @@ class single_level_column_mux_array(design.design): def add_pins(self): for i in range(self.columns): - self.add_pin("bl[{}]".format(i)) - self.add_pin("br[{}]".format(i)) + self.add_pin("bl_{}".format(i)) + self.add_pin("br_{}".format(i)) for i in range(self.words_per_row): - self.add_pin("sel[{}]".format(i)) + self.add_pin("sel_{}".format(i)) for i in range(self.word_size): - self.add_pin("bl_out[{}]".format(i)) - self.add_pin("br_out[{}]".format(i)) + self.add_pin("bl_out_{}".format(i)) + self.add_pin("br_out_{}".format(i)) self.add_pin("gnd") def add_modules(self): - # FIXME: Why is this 8x? - self.mux = single_level_column_mux(tx_size=8, bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br) + self.mux = single_level_column_mux(bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br) self.add_mod(self.mux) @@ -83,11 +82,11 @@ class single_level_column_mux_array(design.design): self.mux_inst.append(self.add_inst(name=name, mod=self.mux)) - self.connect_inst(["bl[{}]".format(col_num), - "br[{}]".format(col_num), - "bl_out[{}]".format(int(col_num/self.words_per_row)), - "br_out[{}]".format(int(col_num/self.words_per_row)), - "sel[{}]".format(col_num % self.words_per_row), + self.connect_inst(["bl_{}".format(col_num), + "br_{}".format(col_num), + "bl_out_{}".format(int(col_num/self.words_per_row)), + "br_out_{}".format(int(col_num/self.words_per_row)), + "sel_{}".format(col_num % self.words_per_row), "gnd"]) def place_array(self): @@ -104,13 +103,13 @@ class single_level_column_mux_array(design.design): for col_num in range(self.columns): mux_inst = self.mux_inst[col_num] offset = mux_inst.get_pin("bl").ll() - self.add_layout_pin(text="bl[{}]".format(col_num), + self.add_layout_pin(text="bl_{}".format(col_num), layer="metal2", offset=offset, height=self.height-offset.y) offset = mux_inst.get_pin("br").ll() - self.add_layout_pin(text="br[{}]".format(col_num), + self.add_layout_pin(text="br_{}".format(col_num), layer="metal2", offset=offset, height=self.height-offset.y) @@ -128,7 +127,7 @@ class single_level_column_mux_array(design.design): """ Create address input rails on M1 below the mux transistors """ for j in range(self.words_per_row): offset = vector(0, self.route_height + (j-self.words_per_row)*self.m1_pitch) - self.add_layout_pin(text="sel[{}]".format(j), + self.add_layout_pin(text="sel_{}".format(j), layer="metal1", offset=offset, width=self.mux.width * self.columns, @@ -144,9 +143,9 @@ class single_level_column_mux_array(design.design): # Add the column x offset to find the right select bit gate_offset = self.mux_inst[col].get_pin("sel").bc() # height to connect the gate to the correct horizontal row - sel_height = self.get_pin("sel[{}]".format(sel_index)).by() + sel_height = self.get_pin("sel_{}".format(sel_index)).by() # use the y offset from the sel pin and the x offset from the gate - offset = vector(gate_offset.x,self.get_pin("sel[{}]".format(sel_index)).cy()) + offset = vector(gate_offset.x,self.get_pin("sel_{}".format(sel_index)).cy()) # Add the poly contact with a shift to account for the rotation self.add_via_center(layers=("metal1", "contact", "poly"), offset=offset, @@ -170,23 +169,23 @@ class single_level_column_mux_array(design.design): self.add_rect(layer="metal1", offset=bl_out_offset, width=width, - height=drc["minwidth_metal2"]) + height=drc("minwidth_metal2")) self.add_rect(layer="metal1", offset=br_out_offset, width=width, - height=drc["minwidth_metal2"]) + height=drc("minwidth_metal2")) # Extend the bitline output rails and gnd downward on the first bit of each n-way mux - self.add_layout_pin(text="bl_out[{}]".format(int(j/self.words_per_row)), + self.add_layout_pin(text="bl_out_{}".format(int(j/self.words_per_row)), layer="metal2", offset=bl_out_offset.scale(1,0), - width=drc['minwidth_metal2'], + width=drc('minwidth_metal2'), height=self.route_height) - self.add_layout_pin(text="br_out[{}]".format(int(j/self.words_per_row)), + self.add_layout_pin(text="br_out_{}".format(int(j/self.words_per_row)), layer="metal2", offset=br_out_offset.scale(1,0), - width=drc['minwidth_metal2'], + width=drc('minwidth_metal2'), height=self.route_height) # This via is on the right of the wire @@ -202,7 +201,7 @@ class single_level_column_mux_array(design.design): self.add_rect(layer="metal2", offset=bl_out_offset, - width=drc['minwidth_metal2'], + width=drc('minwidth_metal2'), height=self.route_height-bl_out_offset.y) # This via is on the right of the wire self.add_via(layers=("metal1", "via1", "metal2"), @@ -210,12 +209,20 @@ class single_level_column_mux_array(design.design): rotate=90) self.add_rect(layer="metal2", offset=br_out_offset, - width=drc['minwidth_metal2'], + width=drc('minwidth_metal2'), height=self.route_height-br_out_offset.y) # This via is on the left of the wire self.add_via(layers=("metal1", "via1", "metal2"), offset= br_out_offset, rotate=90) - + def analytical_delay(self, vdd, slew, load=0.0): + from tech import spice, parameter + r = spice["min_tx_r"]/(self.mux.ptx_width/parameter["min_tx_size"]) + #Drains of mux transistors make up capacitance. + c_para = spice["min_tx_drain_c"]*(self.mux.ptx_width/parameter["min_tx_size"])*self.words_per_row#ff + volt_swing = spice["v_threshold_typical"]/vdd + + result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew, swing = volt_swing) + return self.return_delay(result.delay, result.slew) diff --git a/compiler/modules/tri_gate.py b/compiler/modules/tri_gate.py index cce7683c..da42c7a8 100644 --- a/compiler/modules/tri_gate.py +++ b/compiler/modules/tri_gate.py @@ -12,7 +12,7 @@ class tri_gate(design.design): pin_names = ["in", "en", "en_bar", "out", "gnd", "vdd"] (width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"]) unique_id = 1 diff --git a/compiler/modules/tri_gate_array.py b/compiler/modules/tri_gate_array.py index f8b939af..5ca992b3 100644 --- a/compiler/modules/tri_gate_array.py +++ b/compiler/modules/tri_gate_array.py @@ -45,9 +45,9 @@ class tri_gate_array(design.design): def add_pins(self): """create the name of pins depend on the word size""" for i in range(self.word_size): - self.add_pin("in[{0}]".format(i)) + self.add_pin("in_{0}".format(i)) for i in range(self.word_size): - self.add_pin("out[{0}]".format(i)) + self.add_pin("out_{0}".format(i)) for pin in ["en", "en_bar", "vdd", "gnd"]: self.add_pin(pin) @@ -59,8 +59,8 @@ class tri_gate_array(design.design): self.tri_inst[i]=self.add_inst(name=name, mod=self.tri) index = int(i/self.words_per_row) - self.connect_inst(["in[{0}]".format(index), - "out[{0}]".format(index), + self.connect_inst(["in_{0}".format(index), + "out_{0}".format(index), "en", "en_bar", "vdd", "gnd"]) def place_array(self): @@ -76,14 +76,14 @@ class tri_gate_array(design.design): index = int(i/self.words_per_row) in_pin = self.tri_inst[i].get_pin("in") - self.add_layout_pin(text="in[{0}]".format(index), + self.add_layout_pin(text="in_{0}".format(index), layer="metal2", offset=in_pin.ll(), width=in_pin.width(), height=in_pin.height()) out_pin = self.tri_inst[i].get_pin("out") - self.add_layout_pin(text="out[{0}]".format(index), + self.add_layout_pin(text="out_{0}".format(index), layer="metal2", offset=out_pin.ll(), width=out_pin.width(), @@ -107,14 +107,14 @@ class tri_gate_array(design.design): layer="metal1", offset=en_pin.ll().scale(0, 1), width=width, - height=drc["minwidth_metal1"]) + height=drc("minwidth_metal1")) enbar_pin = self.tri_inst[0].get_pin("en_bar") self.add_layout_pin(text="en_bar", layer="metal1", offset=enbar_pin.ll().scale(0, 1), width=width, - height=drc["minwidth_metal1"]) + height=drc("minwidth_metal1")) diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 277e8003..af82fbc5 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -40,11 +40,11 @@ class wordline_driver(design.design): def add_pins(self): # inputs to wordline_driver. for i in range(self.rows): - self.add_pin("in[{0}]".format(i)) + self.add_pin("in_{0}".format(i)) # Outputs from wordline_driver. for i in range(self.rows): - self.add_pin("wl[{0}]".format(i)) - self.add_pin("en") + self.add_pin("wl_{0}".format(i)) + self.add_pin("en_bar") self.add_pin("vdd") self.add_pin("gnd") @@ -67,7 +67,7 @@ class wordline_driver(design.design): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ # Find the x offsets for where the vias/pins should be placed - a_xoffset = self.inv1_inst[0].rx() + a_xoffset = self.nand_inst[0].rx() b_xoffset = self.inv2_inst[0].lx() for num in range(self.rows): # this will result in duplicate polygons for rails, but who cares @@ -95,59 +95,45 @@ class wordline_driver(design.design): def create_drivers(self): - self.inv1_inst = [] self.nand_inst = [] self.inv2_inst = [] for row in range(self.rows): - name_inv1 = "wl_driver_inv_en{}".format(row) name_nand = "wl_driver_nand{}".format(row) name_inv2 = "wl_driver_inv{}".format(row) - # add inv1 based on the info above - self.inv1_inst.append(self.add_inst(name=name_inv1, - mod=self.inv_no_output)) - self.connect_inst(["en", - "en_bar[{0}]".format(row), - "vdd", "gnd"]) # add nand 2 self.nand_inst.append(self.add_inst(name=name_nand, mod=self.nand2)) - self.connect_inst(["en_bar[{0}]".format(row), - "in[{0}]".format(row), - "wl_bar[{0}]".format(row), + self.connect_inst(["en_bar", + "in_{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)) - self.connect_inst(["wl_bar[{0}]".format(row), - "wl[{0}]".format(row), + self.connect_inst(["wl_bar_{0}".format(row), + "wl_{0}".format(row), "vdd", "gnd"]) def place_drivers(self): - inv1_xoffset = 2*self.m1_width + 5*self.m1_space - nand2_xoffset = inv1_xoffset + self.inv.width + nand2_xoffset = 2*self.m1_width + 5*self.m1_space inv2_xoffset = nand2_xoffset + self.nand2.width - self.width = inv2_xoffset + self.inv.height - driver_height = self.inv.height + self.width = inv2_xoffset + self.inv.width self.height = self.inv.height * self.rows for row in range(self.rows): if (row % 2): - y_offset = driver_height*(row + 1) + y_offset = self.inv.height*(row + 1) inst_mirror = "MX" else: - y_offset = driver_height*row + y_offset = self.inv.height*row inst_mirror = "R0" - inv1_offset = [inv1_xoffset, y_offset] nand2_offset=[nand2_xoffset, y_offset] inv2_offset=[inv2_xoffset, y_offset] - # add inv1 based on the info above - self.inv1_inst[row].place(offset=inv1_offset, - mirror=inst_mirror) # add nand 2 self.nand_inst[row].place(offset=nand2_offset, mirror=inst_mirror) @@ -160,7 +146,7 @@ class wordline_driver(design.design): """ Route all of the signals """ # Wordline enable connection - en_pin=self.add_layout_pin(text="en", + en_pin=self.add_layout_pin(text="en_bar", layer="metal2", offset=[self.m1_width + 2*self.m1_space,0], width=self.m2_width, @@ -168,12 +154,11 @@ class wordline_driver(design.design): for row in range(self.rows): - inv1_inst = self.inv1_inst[row] nand_inst = self.nand_inst[row] inv2_inst = self.inv2_inst[row] - # en connection - a_pin = inv1_inst.get_pin("A") + # en_bar connection + a_pin = nand_inst.get_pin("A") a_pos = a_pin.lc() clk_offset = vector(en_pin.bc().x,a_pos.y) self.add_segment_center(layer="metal1", @@ -182,13 +167,6 @@ class wordline_driver(design.design): self.add_via_center(layers=("metal1", "via1", "metal2"), offset=clk_offset) - # first inv to nand2 A - zb_pos = inv1_inst.get_pin("Z").bc() - zu_pos = inv1_inst.get_pin("Z").uc() - bl_pos = nand_inst.get_pin("A").lc() - br_pos = nand_inst.get_pin("A").rc() - self.add_path("metal1", [zb_pos, zu_pos, bl_pos, br_pos]) - # Nand2 out to 2nd inv zr_pos = nand_inst.get_pin("Z").rc() al_pos = inv2_inst.get_pin("A").lc() @@ -205,7 +183,7 @@ class wordline_driver(design.design): input_offset = vector(0,b_pos.y + up_or_down) mid_via_offset = vector(clk_offset.x,input_offset.y) + vector(0.5*self.m2_width+self.m2_space+0.5*contact.m1m2.width,0) # must under the clk line in M1 - self.add_layout_pin_segment_center(text="in[{0}]".format(row), + self.add_layout_pin_segment_center(text="in_{0}".format(row), layer="metal1", start=input_offset, end=mid_via_offset) @@ -221,7 +199,7 @@ class wordline_driver(design.design): # output each WL on the right wl_offset = inv2_inst.get_pin("Z").rc() - self.add_layout_pin_segment_center(text="wl[{0}]".format(row), + self.add_layout_pin_segment_center(text="wl_{0}".format(row), layer="metal1", start=wl_offset, end=wl_offset-vector(self.m1_width,0)) @@ -238,4 +216,25 @@ class wordline_driver(design.design): def input_load(self): + """Gets the capacitance of the wordline driver in absolute units (fF)""" return self.nand2.input_load() + + def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): + """Follows the clk_buf to a wordline signal adding each stages stage effort to a list""" + stage_effort_list = [] + + stage1_cout = self.inv.get_cin() + stage1 = self.nand2.get_effort_stage(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_effort_stage(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_wl_en_cin(self): + """Get the relative capacitance of all the enable connections in the bank""" + #The enable is connected to a nand2 for every row. + total_cin = self.nand2.get_cin() * self.rows + return total_cin \ No newline at end of file diff --git a/compiler/modules/write_driver.py b/compiler/modules/write_driver.py index 67477a8d..8d1291a7 100644 --- a/compiler/modules/write_driver.py +++ b/compiler/modules/write_driver.py @@ -13,7 +13,7 @@ class write_driver(design.design): pin_names = ["din", "bl", "br", "en", "gnd", "vdd"] (width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"]) def __init__(self, name): design.design.__init__(self, name) diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index eff0c8d8..3b5e75d9 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -44,10 +44,10 @@ class write_driver_array(design.design): def add_pins(self): for i in range(self.word_size): - self.add_pin("data[{0}]".format(i)) + self.add_pin("data_{0}".format(i)) for i in range(self.word_size): - self.add_pin("bl[{0}]".format(i)) - self.add_pin("br[{0}]".format(i)) + self.add_pin("bl_{0}".format(i)) + self.add_pin("br_{0}".format(i)) self.add_pin("en") self.add_pin("vdd") self.add_pin("gnd") @@ -68,14 +68,14 @@ class write_driver_array(design.design): def create_write_array(self): self.driver_insts = {} for i in range(0,self.columns,self.words_per_row): - name = "Xwrite_driver{}".format(i) + name = "write_driver{}".format(i) index = int(i/self.words_per_row) self.driver_insts[index]=self.add_inst(name=name, mod=self.driver) - self.connect_inst(["data[{0}]".format(index), - "bl[{0}]".format(index), - "br[{0}]".format(index), + self.connect_inst(["data_{0}".format(index), + "bl_{0}".format(index), + "br_{0}".format(index), "en", "vdd", "gnd"]) @@ -94,20 +94,20 @@ class write_driver_array(design.design): def add_layout_pins(self): for i in range(self.word_size): din_pin = self.driver_insts[i].get_pin("din") - self.add_layout_pin(text="data[{0}]".format(i), + self.add_layout_pin(text="data_{0}".format(i), layer="metal2", offset=din_pin.ll(), width=din_pin.width(), height=din_pin.height()) bl_pin = self.driver_insts[i].get_pin("bl") - self.add_layout_pin(text="bl[{0}]".format(i), + self.add_layout_pin(text="bl_{0}".format(i), layer="metal2", offset=bl_pin.ll(), width=bl_pin.width(), height=bl_pin.height()) br_pin = self.driver_insts[i].get_pin("br") - self.add_layout_pin(text="br[{0}]".format(i), + self.add_layout_pin(text="br_{0}".format(i), layer="metal2", offset=br_pin.ll(), width=br_pin.width(), @@ -130,7 +130,7 @@ class write_driver_array(design.design): layer="metal1", offset=self.driver_insts[0].get_pin("en").ll().scale(0,1), width=self.width, - height=drc['minwidth_metal1']) + height=drc('minwidth_metal1')) diff --git a/compiler/openram.py b/compiler/openram.py index e4ab593e..78241f6a 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -7,7 +7,6 @@ a spice (.sp) file for circuit simulation a GDS2 (.gds) file containing the layout a LEF (.lef) file for preliminary P&R (real one should be from layout) a Liberty (.lib) file for timing analysis/optimization - """ import sys,os @@ -27,36 +26,36 @@ if len(args) != 1: # These depend on arguments, so don't load them until now. import debug - init_openram(config_file=args[0], is_unit_test=False) # Only print banner here so it's not in unit tests print_banner() -# Output info about this run -report_status() - -# Start importing design modules after we have the config file -import verify -from sram import sram -from sram_config import sram_config - -output_extensions = ["sp","v","lib"] -if not OPTS.netlist_only: - output_extensions.extend(["gds","lef"]) -output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in output_extensions] -print("Output files are: ") -print(*output_files,sep="\n") - # Keep track of running stats start_time = datetime.datetime.now() print_time("Start",start_time) +# Output info about this run +report_status() + +from sram_config import sram_config + + # Configure the SRAM organization c = sram_config(word_size=OPTS.word_size, num_words=OPTS.num_words) +print("Words per row: {}".format(c.words_per_row)) -# import SRAM test generation +#from parser import * +output_extensions = ["sp","v","lib","py","html"] +if not OPTS.netlist_only: + output_extensions.extend(["gds","lef"]) +output_files = ["{0}{1}.{2}".format(OPTS.output_path,OPTS.output_name,x) for x in output_extensions] +print("Output files are: ") +print(*output_files,sep="\n") + + +from sram import sram s = sram(sram_config=c, name=OPTS.output_name) diff --git a/compiler/options.py b/compiler/options.py index 7781f966..bd4bf607 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -20,8 +20,10 @@ class options(optparse.Values): debug_level = 0 # When enabled, layout is not generated (and no DRC or LVS are performed) netlist_only = False - # This determines whether LVS and DRC is checked for each submodule. + # This determines whether LVS and DRC is checked at all. check_lvsdrc = True + # This determines whether LVS and DRC is checked for every submodule. + inline_lvsdrc = False # Variable to select the variant of spice spice_name = "" # The spice executable being used which is derived from the user PATH. @@ -50,8 +52,6 @@ class options(optparse.Values): analytical_delay = True # Purge the temp directory after a successful run (doesn't purge on errors, anyhow) purge_temp = True - # Determines whether multi-port portion of unit tests are run or not - multiport_check = True # These are the configuration parameters num_rw_ports = 1 diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py new file mode 100644 index 00000000..5eb1ceb9 --- /dev/null +++ b/compiler/pgates/pand2.py @@ -0,0 +1,139 @@ +import debug +from tech import drc +from math import log +from vector import vector +from globals import OPTS +from pnand2 import pnand2 +from pinv import pinv +import pgate + +class pand2(pgate.pgate): + """ + This is a simple buffer used for driving loads. + """ + from importlib import reload + c = reload(__import__(OPTS.bitcell)) + bitcell = getattr(c, OPTS.bitcell) + + unique_id = 1 + + def __init__(self, size=1, height=None, name=""): + + self.size = size + + if name=="": + name = "pand2_{0}_{1}".format(size, pand2.unique_id) + pand2.unique_id += 1 + + pgate.pgate.__init__(self, name, height) + debug.info(1, "Creating {}".format(self.name)) + + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + # Shield the cap, but have at least a stage effort of 4 + self.nand = pnand2(height=self.height) + self.add_mod(self.nand) + + self.inv = pinv(size=self.size, height=self.height) + self.add_mod(self.inv) + + def create_layout(self): + self.width = self.nand.width + self.inv.width + self.place_insts() + self.add_wires() + self.add_layout_pins() + + def add_pins(self): + self.add_pin("A") + self.add_pin("B") + self.add_pin("Z") + self.add_pin("vdd") + self.add_pin("gnd") + + def create_insts(self): + self.nand_inst=self.add_inst(name="pand2_nand", + mod=self.nand) + self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) + + self.inv_inst=self.add_inst(name="pand2_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0,0)) + + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(),0)) + + def add_wires(self): + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.inv_inst.get_pin("A") + mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) + mid2_point = vector(mid1_point, a2_pin.cy()) + self.add_path("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) + + + def add_layout_pins(self): + # Continous vdd rail along with label. + vdd_pin=self.inv_inst.get_pin("vdd") + self.add_layout_pin(text="vdd", + layer="metal1", + offset=vdd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + # Continous gnd rail along with label. + gnd_pin=self.inv_inst.get_pin("gnd") + self.add_layout_pin(text="gnd", + layer="metal1", + offset=gnd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + pin = self.inv_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A","B"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + + + def analytical_delay(self, slew, load=0.0): + """ Calculate the analytical delay of DFF-> INV -> INV """ + nand_delay = selfnand.analytical_delay(slew=slew, load=self.inv.input_load()) + inv_delay = self.inv.analytical_delay(slew=nand_delay.slew, load=load) + return nand_delay + inv_delay + + def get_output_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv.get_cin() + stage1 = self.nand.get_effort_stage(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_effort_stage(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py deleted file mode 100644 index 24fdfb60..00000000 --- a/compiler/pgates/pbitcell.py +++ /dev/null @@ -1,1217 +0,0 @@ -import contact -import design -import debug -from tech import drc, parameter, spice -from vector import vector -from ptx import ptx -from globals import OPTS - -class pbitcell(design.design): - """ - This module implements a parametrically sized multi-port bitcell, - with a variable number of read/write, write, and read ports - """ - - def __init__(self): - - self.num_rw_ports = OPTS.num_rw_ports - self.num_w_ports = OPTS.num_w_ports - self.num_r_ports = OPTS.num_r_ports - self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports - - name = "pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports) - # This is not a pgate because pgates depend on the bitcell height! - design.design.__init__(self, name) - debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, - self.num_w_ports, - self.num_r_ports)) - - self.create_netlist() - # We must always create the bitcell layout because - # some transistor sizes in the other netlists depend on it - self.create_layout() - - - def create_netlist(self): - self.add_pins() - self.add_modules() - self.create_storage() - - if(self.num_rw_ports > 0): - self.create_readwrite_ports() - if(self.num_w_ports > 0): - self.create_write_ports() - if(self.num_r_ports > 0): - self.create_read_ports() - - def create_layout(self): - self.calculate_spacing() - self.calculate_postions() - - self.place_storage() - self.route_storage() - self.route_rails() - - if(self.num_rw_ports > 0): - self.place_readwrite_ports() - self.route_readwrite_wordlines() - self.route_readwrite_bitlines() - if(self.num_w_ports == 0): # routing for write to storage is the same as read/write to storage - self.route_readwrite_access() - if(self.num_w_ports > 0): - self.place_write_ports() - self.route_write_wordlines() - self.route_write_bitlines() - self.route_write_access() - if(self.num_r_ports > 0): - self.place_read_ports() - self.route_read_wordlines() - self.route_read_bitlines() - self.route_read_access() - self.extend_well() - - self.offset_all_coordinates() - self.DRC_LVS() - - def add_pins(self): - self.rw_bl_names = [] - self.rw_br_names = [] - self.w_bl_names = [] - self.w_br_names = [] - self.r_bl_names = [] - self.r_br_names = [] - self.rw_wl_names = [] - self.w_wl_names = [] - self.r_wl_names = [] - port = 0 - - for k in range(self.num_rw_ports): - self.add_pin("bl{}".format(port)) - self.add_pin("br{}".format(port)) - self.rw_bl_names.append("bl{}".format(port)) - self.rw_br_names.append("br{}".format(port)) - port += 1 - for k in range(self.num_w_ports): - self.add_pin("bl{}".format(port)) - self.add_pin("br{}".format(port)) - self.w_bl_names.append("bl{}".format(port)) - self.w_br_names.append("br{}".format(port)) - port += 1 - for k in range(self.num_r_ports): - self.add_pin("bl{}".format(port)) - self.add_pin("br{}".format(port)) - self.r_bl_names.append("bl{}".format(port)) - self.r_br_names.append("br{}".format(port)) - port += 1 - - port = 0 - for k in range(self.num_rw_ports): - self.add_pin("wl{}".format(port)) - self.rw_wl_names.append("wl{}".format(port)) - port += 1 - for k in range(self.num_w_ports): - self.add_pin("wl{}".format(port)) - self.w_wl_names.append("wl{}".format(port)) - port += 1 - for k in range(self.num_r_ports): - self.add_pin("wl{}".format(port)) - self.r_wl_names.append("wl{}".format(port)) - port += 1 - - self.add_pin("vdd") - self.add_pin("gnd") - - - def add_modules(self): - """ - Determine size of transistors and add ptx modules - """ - # if there are any read/write ports, then the inverter nmos is sized based the number of read/write ports - if(self.num_rw_ports > 0): - inverter_nmos_width = self.num_rw_ports*3*parameter["min_tx_size"] - inverter_pmos_width = parameter["min_tx_size"] - readwrite_nmos_width = 1.5*parameter["min_tx_size"] - write_nmos_width = parameter["min_tx_size"] - read_nmos_width = 2*parameter["min_tx_size"] - - # if there are no read/write ports, then the inverter nmos is statically sized for the dual port case - else: - inverter_nmos_width = 2*parameter["min_tx_size"] - inverter_pmos_width = parameter["min_tx_size"] - readwrite_nmos_width = 1.5*parameter["min_tx_size"] - write_nmos_width = parameter["min_tx_size"] - read_nmos_width = 2*parameter["min_tx_size"] - - # create ptx for inverter transistors - self.inverter_nmos = ptx(width=inverter_nmos_width, - tx_type="nmos") - self.add_mod(self.inverter_nmos) - - self.inverter_pmos = ptx(width=inverter_pmos_width, - tx_type="pmos") - self.add_mod(self.inverter_pmos) - - # create ptx for readwrite transitors - self.readwrite_nmos = ptx(width=readwrite_nmos_width, - tx_type="nmos") - self.add_mod(self.readwrite_nmos) - - # create ptx for write transitors - self.write_nmos = ptx(width=write_nmos_width, - tx_type="nmos") - self.add_mod(self.write_nmos) - - # create ptx for read transistors - self.read_nmos = ptx(width=read_nmos_width, - tx_type="nmos") - self.add_mod(self.read_nmos) - - def calculate_spacing(self): - """ Calculate transistor spacings """ - - # calculate metal contact extensions over transistor active - self.inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height) - self.readwrite_nmos_contact_extension = 0.5*(self.readwrite_nmos.active_contact.height - self.readwrite_nmos.active_height) - self.write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height) - self.read_nmos_contact_extension = 0.5*(self.read_nmos.active_contact.height - self.read_nmos.active_height) - - # calculate the distance threshold for different gate contact spacings - self.gate_contact_thres = drc["poly_to_active"] - drc["minwidth_metal2"] - - #calculations for horizontal transistor to tansistor spacing - # inverter spacings - self.inverter_to_inverter_spacing = contact.poly.height + drc["minwidth_metal1"] - self.inverter_to_write_spacing = drc["pwell_to_nwell"] + 2*drc["well_enclosure_active"] - - # readwrite to readwrite transistor spacing (also acts as readwrite to write transistor spacing) - if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): - self.readwrite_to_readwrite_spacing = drc["minwidth_metal2"] + self.readwrite_nmos_contact_extension + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] - else: - self.readwrite_to_readwrite_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] - - # write to write transistor spacing - if(self.write_nmos_contact_extension > self.gate_contact_thres): - self.write_to_write_spacing = drc["minwidth_metal2"] + self.write_nmos_contact_extension + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] - else: - self.write_to_write_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] - - # read to read transistor spacing - if(self.read_nmos_contact_extension > self.gate_contact_thres): - self.read_to_read_spacing = 2*(drc["minwidth_metal2"] + self.read_nmos_contact_extension) + drc["minwidth_metal1"] + 2*contact.poly.width - else: - self.read_to_read_spacing = 2*drc["poly_to_active"] + drc["minwidth_metal1"] + 2*contact.poly.width - - # write to read transistor spacing (also acts as readwrite to read transistor spacing) - # calculation is dependent on whether the read transistor is adjacent to a write transistor or a readwrite transistor - if(self.num_w_ports > 0): - if(self.write_nmos_contact_extension > self.gate_contact_thres): - write_portion = drc["minwidth_metal2"] + self.write_nmos_contact_extension - else: - write_portion = drc["poly_to_active"] - else: - if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): - write_portion = drc["minwidth_metal2"] + self.readwrite_nmos_contact_extension - else: - write_portion = drc["poly_to_active"] - - if(self.read_nmos_contact_extension > self.gate_contact_thres): - read_portion = drc["minwidth_metal2"] + self.read_nmos_contact_extension - else: - read_portion = drc["poly_to_active"] - - self.write_to_read_spacing = write_portion + read_portion + 2*contact.poly.width + drc["poly_to_polycontact"] - - # calculations for transistor tiling (transistor + spacing) - self.inverter_tile_width = self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing - self.readwrite_tile_width = self.readwrite_to_readwrite_spacing + self.readwrite_nmos.active_height - self.write_tile_width = self.write_to_write_spacing + self.write_nmos.active_height - self.read_tile_width = self.read_to_read_spacing + self.read_nmos.active_height - - # calculation for row line tiling - self.rail_tile_height = drc["active_to_body_active"] + contact.well.width - self.rowline_tile_height = drc["minwidth_metal1"] + contact.m1m2.width - - # calculations related to inverter connections - self.inverter_gap = drc["poly_to_active"] + drc["poly_to_polycontact"] + 2*contact.poly.width + drc["minwidth_metal1"] + self.inverter_pmos_contact_extension - self.cross_couple_lower_ypos = self.inverter_nmos.active_height + drc["poly_to_active"] + 0.5*contact.poly.width - self.cross_couple_upper_ypos = self.inverter_nmos.active_height + drc["poly_to_active"] + drc["poly_to_polycontact"] + 1.5*contact.poly.width - - - def calculate_postions(self): - """ - Calculate positions that describe the edges and dimensions of the cell - """ - # create flags for excluding readwrite, write, or read port calculations if they are not included in the bitcell - if(self.num_rw_ports > 0): - self.readwrite_port_flag = True - else: - self.readwrite_port_flag = False - - if(self.num_w_ports > 0): - self.write_port_flag = True - else: - self.write_port_flag = False - - if(self.num_r_ports > 0): - self.read_port_flag = True - else: - self.read_port_flag = False - - # determine the distance of the leftmost/rightmost transistor gate connection - if (self.num_r_ports > 0): - if(self.read_nmos_contact_extension > self.gate_contact_thres): - end_connection = drc["minwidth_metal2"] + self.read_nmos_contact_extension + contact.m1m2.height - else: - end_connection = drc["poly_to_active"] + contact.m1m2.height - else: - if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): - end_connection = drc["minwidth_metal2"] + self.readwrite_nmos_contact_extension + contact.m1m2.height - else: - end_connection = drc["poly_to_active"] + contact.m1m2.height - - # leftmost position = storage width + read/write ports width + write ports width + read ports width + end transistor gate connections + metal spacing necessary for tiling the bitcell - self.leftmost_xpos = -self.inverter_tile_width \ - - self.inverter_to_write_spacing \ - - self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_rw_ports-1)*self.readwrite_tile_width) \ - - self.write_port_flag*self.readwrite_port_flag*self.write_to_write_spacing \ - - self.write_port_flag*(self.write_nmos.active_height + (self.num_w_ports-1)*self.write_tile_width) \ - - self.read_port_flag*self.write_to_read_spacing \ - - self.read_port_flag*(self.read_nmos.active_height + (self.num_r_ports-1)*self.read_tile_width) \ - - end_connection \ - - 0.5*drc["poly_to_polycontact"] - - self.rightmost_xpos = -self.leftmost_xpos - - # bottommost position = gnd height + rwwl height + wwl height + rwl height + space needed between tiled bitcells - array_tiling_offset = 0.5*drc["minwidth_metal2"] - self.botmost_ypos = -self.rail_tile_height \ - - self.num_rw_ports*self.rowline_tile_height \ - - self.num_w_ports*self.rowline_tile_height \ - - self.num_r_ports*self.rowline_tile_height \ - - array_tiling_offset - - # topmost position = height of the inverter + height of vdd - self.topmost_ypos = self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height \ - + self.rail_tile_height - - # calculations for the cell dimensions - array_vdd_overlap = 0.5*contact.well.width - self.width = -2*self.leftmost_xpos - self.height = self.topmost_ypos - self.botmost_ypos - array_vdd_overlap - - - def create_storage(self): - """ - Creates the crossed coupled inverters that act as storage for the bitcell. - The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar". - """ - - # create active for nmos - self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", - mod=self.inverter_nmos) - self.connect_inst(["Q_bar", "Q", "gnd", "gnd"]) - - self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right", - mod=self.inverter_nmos) - self.connect_inst(["gnd", "Q_bar", "Q", "gnd"]) - - # create active for pmos - self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left", - mod=self.inverter_pmos) - self.connect_inst(["Q_bar", "Q", "vdd", "vdd"]) - - self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right", - mod=self.inverter_pmos) - self.connect_inst(["vdd", "Q_bar", "Q", "vdd"]) - - - def place_storage(self): - """ - Places the transistors for the crossed coupled inverters in the bitcell - """ - - # calculate transistor offsets - left_inverter_xpos = -0.5*self.inverter_to_inverter_spacing - self.inverter_nmos.active_width - right_inverter_xpos = 0.5*self.inverter_to_inverter_spacing - inverter_pmos_ypos = self.inverter_nmos.active_height + self.inverter_gap - - # create active for nmos - self.inverter_nmos_left.place([left_inverter_xpos,0]) - self.inverter_nmos_right.place([right_inverter_xpos,0]) - - # create active for pmos - self.inverter_pmos_left.place([left_inverter_xpos, inverter_pmos_ypos]) - self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos]) - - def route_storage(self): - """ - Routes inputs and outputs of inverters to cross couple them - """ - # connect input (gate) of inverters - self.add_path("poly", [self.inverter_nmos_left.get_pin("G").uc(), self.inverter_pmos_left.get_pin("G").bc()]) - self.add_path("poly", [self.inverter_nmos_right.get_pin("G").uc(), self.inverter_pmos_right.get_pin("G").bc()]) - - # connect output (drain/source) of inverters - self.add_path("metal1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], width=contact.well.second_layer_width) - self.add_path("metal1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], width=contact.well.second_layer_width) - - # add contacts to connect gate poly to drain/source metal1 (to connect Q to Q_bar) - contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x + 0.5*contact.poly.height, self.cross_couple_upper_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=contact_offset_left, - rotate=90) - - contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.height, self.cross_couple_lower_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=contact_offset_right, - rotate=90) - - # connect contacts to gate poly (cross couple connections) - gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x, contact_offset_left.y) - self.add_path("poly", [contact_offset_left, gate_offset_right]) - - gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").rc().x, contact_offset_right.y) - self.add_path("poly", [contact_offset_right, gate_offset_left]) - - # update furthest left and right transistor edges (this will propagate to further transistor offset calculations) - self.left_building_edge = -self.inverter_tile_width - self.right_building_edge = self.inverter_tile_width - - - def route_rails(self): - """ - Adds gnd and vdd rails and connects them to the inverters - """ - # Add rails for vdd and gnd - self.gnd_position = vector(self.leftmost_xpos, -self.rail_tile_height) - self.gnd = self.add_layout_pin(text="gnd", - layer="metal1", - offset=self.gnd_position, - width=self.width, - height=contact.well.second_layer_width) - - vdd_ypos = self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height \ - + drc["active_to_body_active"] + 0.5*(drc["minwidth_tx"] - drc["minwidth_metal1"]) - self.vdd_position = vector(self.leftmost_xpos, vdd_ypos) - self.vdd = self.add_layout_pin(text="vdd", - layer="metal1", - offset=self.vdd_position, - width=self.width, - height=drc["minwidth_metal1"]) - - # Connect inverters to rails - # connect inverter nmos to gnd - gnd_pos_left = vector(self.inverter_nmos_left.get_pin("S").bc().x, self.gnd_position.y) - self.add_path("metal1", [self.inverter_nmos_left.get_pin("S").bc(), gnd_pos_left]) - - gnd_pos_right = vector(self.inverter_nmos_right.get_pin("D").bc().x, self.gnd_position.y) - self.add_path("metal1", [self.inverter_nmos_right.get_pin("D").bc(), gnd_pos_right]) - - # connect inverter pmos to vdd - vdd_pos_left = vector(self.inverter_nmos_left.get_pin("S").uc().x, self.vdd_position.y) - self.add_path("metal1", [self.inverter_pmos_left.get_pin("S").uc(), vdd_pos_left]) - - vdd_pos_right = vector(self.inverter_nmos_right.get_pin("D").uc().x, self.vdd_position.y) - self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) - - - def create_readwrite_ports(self): - """ - Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell. - A read or write is enabled by setting a Read-Write-Wordline (RWWL) high, subsequently turning on the transistor. - The transistor is connected between a Read-Write-Bitline (RWBL) and the storage component of the cell (Q). - In a write operation, driving RWBL high or low sets the value of the cell. - In a read operation, RWBL is precharged, then is either remains high or is discharged depending on the value of the cell. - This is a differential design, so each write port has a mirrored port that connects RWBL_bar to Q_bar. - """ - - # define write transistor variables as empty arrays based on the number of write ports - self.readwrite_nmos_left = [None] * self.num_rw_ports - self.readwrite_nmos_right = [None] * self.num_rw_ports - - # iterate over the number of read/write ports - for k in range(0,self.num_rw_ports): - # add read/write transistors - self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k), - mod=self.readwrite_nmos) - self.connect_inst(["Q", self.rw_wl_names[k], self.rw_bl_names[k], "gnd"]) - - self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k), - mod=self.readwrite_nmos) - self.connect_inst(["Q_bar", self.rw_wl_names[k], self.rw_br_names[k], "gnd"]) - - - def place_readwrite_ports(self): - """ - Places read/write ports in the bit cell. - """ - - # Define variables relevant to write transistors - self.rwwl_positions = [None] * self.num_rw_ports - self.rwbl_positions = [None] * self.num_rw_ports - self.rwbl_bar_positions = [None] * self.num_rw_ports - - # define offset correction due to rotation of the ptx module - readwrite_rotation_correct = self.readwrite_nmos.active_height - - # iterate over the number of read/write ports - for k in range(0,self.num_rw_ports): - # Add transistors - # calculate read/write transistor offsets - left_readwrite_transistor_xpos = self.left_building_edge \ - - self.inverter_to_write_spacing \ - - self.readwrite_nmos.active_height - k*self.readwrite_tile_width \ - + readwrite_rotation_correct - - right_readwrite_transistor_xpos = self.right_building_edge \ - + self.inverter_to_write_spacing \ - + k*self.readwrite_tile_width \ - + readwrite_rotation_correct - - # add read/write transistors - self.readwrite_nmos_left[k].place(offset=[left_readwrite_transistor_xpos,0], - rotate=90) - - self.readwrite_nmos_right[k].place(offset=[right_readwrite_transistor_xpos,0], - rotate=90) - - # Add RWWL lines - # calculate RWWL position - rwwl_ypos = self.gnd_position.y - (k+1)*self.rowline_tile_height - self.rwwl_positions[k] = vector(self.leftmost_xpos, rwwl_ypos) - - # add pin for RWWL - self.add_layout_pin(text=self.rw_wl_names[k], - layer="metal1", - offset=self.rwwl_positions[k], - width=self.width, - height=contact.m1m2.width) - - # add pins for RWBL and RWBL_bar, overlaid on source contacts - self.rwbl_positions[k] = vector(self.readwrite_nmos_left[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text=self.rw_bl_names[k], - layer="metal2", - offset=self.rwbl_positions[k], - width=drc["minwidth_metal2"], - height=self.height) - - self.rwbl_bar_positions[k] = vector(self.readwrite_nmos_right[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text=self.rw_br_names[k], - layer="metal2", - offset=self.rwbl_bar_positions[k], - width=drc["minwidth_metal2"], - height=self.height) - - # update furthest left and right transistor edges - self.left_building_edge = left_readwrite_transistor_xpos - self.readwrite_nmos.active_height - self.right_building_edge = right_readwrite_transistor_xpos - - def route_readwrite_wordlines(self): - """ - Routes read/write trnasistors to their respective wordlines - """ - for k in range(0,self.num_rw_ports): - # Gate/RWWL connections - # add poly-to-meltal2 contacts to connect gate of read/write transistors to RWWL (contact next to gate) - # contact must be placed a metal1 width below the source pin to avoid drc from source pin routings - if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.readwrite_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width - else: - contact_xpos = self.readwrite_nmos_left[k].offset.x - self.readwrite_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width - contact_ypos = self.readwrite_nmos_left[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height - left_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_gate_contact) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_gate_contact) - - if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.readwrite_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width - else: - contact_xpos = self.readwrite_nmos_right[k].offset.x + drc["poly_to_active"] + 0.5*contact.poly.width - contact_ypos = self.readwrite_nmos_right[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height - right_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_gate_contact) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_gate_contact) - - # connect gate of read/write transistor to contact (poly path) - midL = vector(left_gate_contact.x, self.readwrite_nmos_left[k].get_pin("G").lc().y) - self.add_path("poly", [self.readwrite_nmos_left[k].get_pin("G").lc(), midL, left_gate_contact], width=contact.poly.width) - - midR = vector(right_gate_contact.x, self.readwrite_nmos_right[k].get_pin("G").rc().y) - self.add_path("poly", [self.readwrite_nmos_right[k].get_pin("G").rc(), midR, right_gate_contact], width=contact.poly.width) - - # add metal1-to-metal2 contacts to RWWL lines - left_rwwl_contact = vector(left_gate_contact.x, self.rwwl_positions[k].y + 0.5*contact.m1m2.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_rwwl_contact, - rotate=90) - - right_rwwl_contact = vector(right_gate_contact.x, self.rwwl_positions[k].y + 0.5*contact.m1m2.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_rwwl_contact, - rotate=90) - - # connect read/write transistor gate contacts to RWWL contacts (metal2 path) - self.add_path("metal2", [left_gate_contact, left_rwwl_contact]) - self.add_path("metal2", [right_gate_contact, right_rwwl_contact]) - - - def route_readwrite_bitlines(self): - """ - Routes read/write transistors to their respective bitlines - """ - for k in range(0,self.num_rw_ports): - # Source/RWBL/RWBL_bar connections - # add metal1-to-metal2 contacts on top of read/write transistor source pins for connection to WBL and WBL_bar - offset_left = self.readwrite_nmos_left[k].get_pin("S").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offset_left, - rotate=90) - - offset_right = self.readwrite_nmos_right[k].get_pin("S").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offset_right, - rotate=90) - - - def route_readwrite_access(self): - """ - Routes read/write transistors to the storage component of the bitcell - """ - last_inst = self.num_rw_ports - 1 - - # Drain/Storage connections - # this path only needs to be drawn once on the last iteration of the loop - # add contacts to connect gate of inverters to drain of read/write transistors - left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_storage_contact, - rotate=90) - - right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_storage_contact, - rotate=90) - - # connect gate of inverters to contacts (poly path) - inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_lower_ypos) - self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) - - inverter_gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_lower_ypos) - self.add_path("poly", [right_storage_contact, inverter_gate_offset_right]) - - # connect contacts to drains of read/write transistors (metal1 path) - midL0 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], left_storage_contact.y) - midL1 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.readwrite_nmos_left[last_inst].get_pin("D").lc().y) - self.add_path("metal1", [left_storage_contact, midL0], width=contact.poly.second_layer_width) # width needed to avoid drc error - self.add_path("metal1", [midL0+vector(0,0.5*contact.poly.second_layer_width), midL1, self.readwrite_nmos_left[last_inst].get_pin("D").lc()]) - - midR0 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], right_storage_contact.y) - midR1 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.readwrite_nmos_right[last_inst].get_pin("D").rc().y) - self.add_path("metal1", [right_storage_contact, midR0], width=contact.poly.second_layer_width) - self.add_path("metal1", [midR0+vector(0,0.5*contact.poly.second_layer_width), midR1, self.readwrite_nmos_right[last_inst].get_pin("D").rc()]) - - def create_write_ports(self): - """ - Creates write ports in the bit cell. A differential pair of transistors can write only. - A write is enabled by setting a Write-Rowline (WWL) high, subsequently turning on the transistor. - The transistor is connected between a Write-Bitline (WBL) and the storage component of the cell (Q). - In a write operation, driving WBL high or low sets the value of the cell. - This is a differential design, so each write port has a mirrored port that connects WBL_bar to Q_bar. - """ - - # Define variables relevant to write transistors - # define offset correction due to rotation of the ptx module - write_rotation_correct = self.write_nmos.active_height - - # define write transistor variables as empty arrays based on the number of write ports - self.write_nmos_left = [None] * self.num_w_ports - self.write_nmos_right = [None] * self.num_w_ports - - # iterate over the number of write ports - for k in range(0,self.num_w_ports): - # add write transistors - self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k), - mod=self.write_nmos) - self.connect_inst(["Q", self.w_wl_names[k], self.w_bl_names[k], "gnd"]) - - self.write_nmos_right[k] = self.add_inst(name="write_nmos_right{}".format(k), - mod=self.write_nmos) - self.connect_inst(["Q_bar", self.w_wl_names[k], self.w_br_names[k], "gnd"]) - - - def place_write_ports(self): - """ - Places write ports in the bit cell. - """ - # Define variables relevant to write transistors - self.wwl_positions = [None] * self.num_w_ports - self.wbl_positions = [None] * self.num_w_ports - self.wbl_bar_positions = [None] * self.num_w_ports - - # define offset correction due to rotation of the ptx module - write_rotation_correct = self.write_nmos.active_height - - # iterate over the number of write ports - for k in range(0,self.num_w_ports): - # Add transistors - # calculate write transistor offsets - left_write_transistor_xpos = self.left_building_edge \ - - (not self.readwrite_port_flag)*self.inverter_to_write_spacing \ - - (self.readwrite_port_flag)*self.readwrite_to_readwrite_spacing \ - - self.write_nmos.active_height - k*self.write_tile_width \ - + write_rotation_correct - - right_write_transistor_xpos = self.right_building_edge \ - + (not self.readwrite_port_flag)*self.inverter_to_write_spacing \ - + (self.readwrite_port_flag)*self.readwrite_to_readwrite_spacing \ - + k*self.write_tile_width \ - + write_rotation_correct - - # add write transistors - self.write_nmos_left[k].place(offset=[left_write_transistor_xpos,0], - rotate=90) - - self.write_nmos_right[k].place(offset=[right_write_transistor_xpos,0], - rotate=90) - - # Add WWL lines - # calculate WWL position - wwl_ypos = self.gnd_position.y \ - - self.num_rw_ports*self.rowline_tile_height \ - - (k+1)*self.rowline_tile_height - self.wwl_positions[k] = vector(self.leftmost_xpos, wwl_ypos) - - # add pin for WWL - self.add_layout_pin(text=self.w_wl_names[k], - layer="metal1", - offset=self.wwl_positions[k], - width=self.width, - height=contact.m1m2.width) - - # add pins for WBL and WBL_bar, overlaid on source contacts - self.wbl_positions[k] = vector(self.write_nmos_left[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text=self.w_bl_names[k], - layer="metal2", - offset=self.wbl_positions[k], - width=drc["minwidth_metal2"], - height=self.height) - - self.wbl_bar_positions[k] = vector(self.write_nmos_right[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text=self.w_br_names[k], - layer="metal2", - offset=self.wbl_bar_positions[k], - width=drc["minwidth_metal2"], - height=self.height) - - # update furthest left and right transistor edges - self.left_building_edge = left_write_transistor_xpos - self.write_nmos.active_height - self.right_building_edge = right_write_transistor_xpos - - def route_write_wordlines(self): - """ - Routes write transistors to their respective wordlines - """ - for k in range(0,self.num_w_ports): - # Gate/WWL connections - # add poly-to-meltal2 contacts to connect gate of write transistors to WWL (contact next to gate) - # contact must be placed a metal width below the source pin to avoid drc from source pin routings - if(self.write_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.write_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width - else: - contact_xpos = self.write_nmos_left[k].offset.x - self.write_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width - contact_ypos = self.write_nmos_left[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height - left_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_gate_contact) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_gate_contact) - - if(self.write_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.write_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width - else: - contact_xpos = self.write_nmos_right[k].offset.x + drc["poly_to_active"] + 0.5*contact.poly.width - contact_ypos = self.write_nmos_right[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height - right_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_gate_contact) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_gate_contact) - - # connect gate of write transistor to contact (poly path) - midL = vector(left_gate_contact.x, self.write_nmos_left[k].get_pin("G").lc().y) - self.add_path("poly", [self.write_nmos_left[k].get_pin("G").lc(), midL, left_gate_contact], width=contact.poly.width) - - midR = vector(right_gate_contact.x, self.write_nmos_right[k].get_pin("G").rc().y) - self.add_path("poly", [self.write_nmos_right[k].get_pin("G").rc(), midR, right_gate_contact], width=contact.poly.width) - - # add metal1-to-metal2 contacts to WWL lines - left_wwl_contact = vector(left_gate_contact.x, self.wwl_positions[k].y + 0.5*contact.m1m2.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_wwl_contact, - rotate=90) - - right_wwl_contact = vector(right_gate_contact.x, self.wwl_positions[k].y + 0.5*contact.m1m2.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_wwl_contact, - rotate=90) - - # connect write transistor gate contacts to WWL contacts (metal2 path) - self.add_path("metal2", [left_gate_contact, left_wwl_contact]) - self.add_path("metal2", [right_gate_contact, right_wwl_contact]) - - def route_write_bitlines(self): - """ - Routes write transistors to their respective bitlines - """ - for k in range(0,self.num_w_ports): - # Source/WBL/WBL_bar connections - # add metal1-to-metal2 contacts on top of write transistor source pins for connection to WBL and WBL_bar - offset_left = self.write_nmos_left[k].get_pin("S").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offset_left, - rotate=90) - - offset_right = self.write_nmos_right[k].get_pin("S").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offset_right, - rotate=90) - - def route_write_access(self): - """ - Routes write transistors to the storage component of the bitcell - """ - last_inst = self.num_w_ports - 1 - - # Drain/Storage connections - # this path only needs to be drawn once on the last iteration of the loop - # add contacts to connect gate of inverters to drain of write transistors - left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_storage_contact, - rotate=90) - - right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_storage_contact, - rotate=90) - - # connect gate of inverters to contacts (poly path) - inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_lower_ypos) - self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) - - inverter_gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_lower_ypos) - self.add_path("poly", [right_storage_contact, inverter_gate_offset_right]) - - # connect contacts to drains of write transistors (metal1 path) - midL0 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], left_storage_contact.y) - midL1 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.write_nmos_left[last_inst].get_pin("D").lc().y) - self.add_path("metal1", [left_storage_contact, midL0], width=contact.poly.second_layer_width) # width needed to avoid drc error - self.add_path("metal1", [midL0+vector(0,0.5*contact.poly.second_layer_width), midL1, self.write_nmos_left[last_inst].get_pin("D").lc()]) - - midR0 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], right_storage_contact.y) - midR1 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.write_nmos_right[last_inst].get_pin("D").rc().y) - self.add_path("metal1", [right_storage_contact, midR0], width=contact.poly.second_layer_width) - self.add_path("metal1", [midR0+vector(0,0.5*contact.poly.second_layer_width), midR1, self.write_nmos_right[last_inst].get_pin("D").rc()]) - - - def create_read_ports(self): - """ - Creates read ports in the bit cell. A differential pair of ports can read only. - Two transistors function as a read port, denoted as the "read transistor" and the "read-access transistor". - The read transistor is connected to RWL (gate), RBL (drain), and the read-access transistor (source). - The read-access transistor is connected to Q_bar (gate), gnd (source), and the read transistor (drain). - A read is enabled by setting a Read-Rowline (RWL) high, subsequently turning on the read transistor. - The Read-Bitline (RBL) is precharged to high, and when the value of Q_bar is high, the read-access transistor - is turned on, creating a connection between RBL and gnd. RBL subsequently discharges allowing for a differential read - using sense amps. This is a differential design, so each read port has a mirrored port that connects RBL_bar to Q. - """ - - # define read transistor variables as empty arrays based on the number of read ports - self.read_nmos_left = [None] * self.num_r_ports - self.read_nmos_right = [None] * self.num_r_ports - self.read_access_nmos_left = [None] * self.num_r_ports - self.read_access_nmos_right = [None] * self.num_r_ports - - # iterate over the number of read ports - for k in range(0,self.num_r_ports): - # add read-access transistors - self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k), - mod=self.read_nmos) - self.connect_inst(["RA_to_R_left{}".format(k), " Q_bar", "gnd", "gnd"]) - - self.read_access_nmos_right[k] = self.add_inst(name="read_access_nmos_right{}".format(k), - mod=self.read_nmos) - self.connect_inst(["RA_to_R_right{}".format(k), "Q", "gnd", "gnd"]) - - # add read transistors - self.read_nmos_left[k] = self.add_inst(name="read_nmos_left{}".format(k), - mod=self.read_nmos) - self.connect_inst([self.r_bl_names[k], self.r_wl_names[k], "RA_to_R_left{}".format(k), "gnd"]) - - self.read_nmos_right[k] = self.add_inst(name="read_nmos_right{}".format(k), - mod=self.read_nmos) - self.connect_inst([self.r_br_names[k], self.r_wl_names[k], "RA_to_R_right{}".format(k), "gnd"]) - - def place_read_ports(self): - """ - Places the read ports in the bit cell. - """ - # Define variables relevant to read transistors - self.rwl_positions = [None] * self.num_r_ports - self.rbl_positions = [None] * self.num_r_ports - self.rbl_bar_positions = [None] * self.num_r_ports - - # define offset correction due to rotation of the ptx module - read_rotation_correct = self.read_nmos.active_height - - # calculate offset to overlap the drain of the read-access transistor with the source of the read transistor - overlap_offset = self.read_nmos.get_pin("D").ll() - self.read_nmos.get_pin("S").ll() - - # iterate over the number of read ports - for k in range(0,self.num_r_ports): - # Add transistors - # calculate transistor offsets - left_read_transistor_xpos = self.left_building_edge \ - - self.write_to_read_spacing \ - - self.read_nmos.active_height - k*self.read_tile_width \ - + read_rotation_correct - - right_read_transistor_xpos = self.right_building_edge \ - + self.write_to_read_spacing \ - + k*self.read_tile_width \ - + read_rotation_correct - - # add read-access transistors - self.read_access_nmos_left[k].place(offset=[left_read_transistor_xpos,0], - rotate=90) - - self.read_access_nmos_right[k].place(offset=[right_read_transistor_xpos,0], - rotate=90) - - # add read transistors - self.read_nmos_left[k].place(offset=[left_read_transistor_xpos,overlap_offset.x], - rotate=90) - - self.read_nmos_right[k].place(offset=[right_read_transistor_xpos,overlap_offset.x], - rotate=90) - - # Add RWL lines - # calculate RWL position - rwl_ypos = self.gnd_position.y \ - - self.num_rw_ports*self.rowline_tile_height \ - - self.num_w_ports*self.rowline_tile_height \ - - (k+1)*self.rowline_tile_height - self.rwl_positions[k] = vector(self.leftmost_xpos, rwl_ypos) - - # add pin for RWL - self.add_layout_pin(text=self.r_wl_names[k], - layer="metal1", - offset=self.rwl_positions[k], - width=self.width, - height=contact.m1m2.width) - - # add pins for RBL and RBL_bar, overlaid on drain contacts - self.rbl_positions[k] = vector(self.read_nmos_left[k].get_pin("D").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text=self.r_bl_names[k], - layer="metal2", - offset=self.rbl_positions[k], - width=drc["minwidth_metal2"], - height=self.height) - - self.rbl_bar_positions[k] = vector(self.read_nmos_right[k].get_pin("D").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos) - self.add_layout_pin(text=self.r_br_names[k], - layer="metal2", - offset=self.rbl_bar_positions[k], - width=drc["minwidth_metal2"], - height=self.height) - - def route_read_wordlines(self): - """ - Routes read transistors to their respective worlines - """ - for k in range(0,self.num_r_ports): - # Gate of read transistor / RWL connection - # add poly-to-meltal2 contacts to connect gate of read transistors to RWL (contact next to gate) - if(self.read_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.read_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width - else: - contact_xpos = self.read_nmos_left[k].offset.x - self.read_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width - contact_ypos = self.read_nmos_left[k].get_pin("G").lc().y - left_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_gate_contact) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_gate_contact) - - if(self.read_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.read_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width - else: - contact_xpos = self.read_nmos_right[k].offset.x + drc["poly_to_active"] + 0.5*contact.poly.width - contact_ypos = self.read_nmos_right[k].get_pin("G").rc().y - right_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_gate_contact) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_gate_contact) - - # connect gate of read transistor to contact (poly path) - self.add_path("poly", [self.read_nmos_left[k].get_pin("G").lc(), left_gate_contact]) - self.add_path("poly", [self.read_nmos_right[k].get_pin("G").rc(), right_gate_contact]) - - # add metal1-to-metal2 contacts to RWL lines - left_rwl_contact = vector(left_gate_contact.x, self.rwl_positions[k].y + 0.5*contact.poly.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=left_rwl_contact, - rotate=90) - - right_rwl_contact = vector(right_gate_contact.x, self.rwl_positions[k].y + 0.5*contact.poly.width) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=right_rwl_contact, - rotate=90) - - # connect read transistor gate contacts to RWL contacts (metal2 path) - self.add_path("metal2", [left_gate_contact, left_rwl_contact]) - self.add_path("metal2", [right_gate_contact, right_rwl_contact]) - - # Source of read-access transistor / GND connection - # connect source of read-access transistor to GND (metal1 path) - gnd_offset_left = vector(self.read_access_nmos_left[k].get_pin("S").bc().x, self.gnd_position.y) - self.add_path("metal1", [self.read_access_nmos_left[k].get_pin("S").bc(), gnd_offset_left]) - - gnd_offset_right = vector(self.read_access_nmos_right[k].get_pin("S").bc().x, self.gnd_position.y) - self.add_path("metal1", [self.read_access_nmos_right[k].get_pin("S").bc(), gnd_offset_right]) - - def route_read_bitlines(self): - """ - Routes read transistors to their respective bitlines - """ - for k in range(0,self.num_r_ports): - # Drain of read transistor / RBL & RBL_bar connection - # add metal1-to-metal2 contacts on top of read transistor drain pins for connection to RBL and RBL_bar - offset_left = self.read_nmos_left[k].get_pin("D").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offset_left, - rotate=90) - - offset_right = self.read_nmos_right[k].get_pin("D").center() - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=offset_right, - rotate=90) - - def route_read_access(self): - """ - Routes read access transistors to the storage component of the bitcell - """ - for k in range(0,self.num_r_ports): - # Gate of read-access transistor / storage connection - # add poly-to-metal1 contacts to connect gate of read-access transistors to output of inverters (contact next to gate) - if(self.read_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.read_nmos_left[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width - else: - contact_xpos = self.read_nmos_left[k].offset.x + drc["poly_to_active"] + 0.5*contact.poly.width - contact_ypos = self.read_access_nmos_left[k].get_pin("G").rc().y - left_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=left_gate_contact) - - if(self.read_nmos_contact_extension > self.gate_contact_thres): - contact_xpos = self.read_nmos_right[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width - else: - contact_xpos = self.read_nmos_right[k].offset.x - self.read_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width - contact_ypos = self.read_access_nmos_right[k].get_pin("G").lc().y - right_gate_contact = vector(contact_xpos, contact_ypos) - - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=right_gate_contact) - - # connect gate of read-access transistor to contact (poly path) - self.add_path("poly", [self.read_access_nmos_left[k].get_pin("G").rc(), left_gate_contact]) - self.add_path("poly", [self.read_access_nmos_right[k].get_pin("G").lc(), right_gate_contact]) - - # save the positions of the first gate contacts for use in later iterations - if(k == 0): - left_gate_contact0 = left_gate_contact - right_gate_contact0 = right_gate_contact - - # connect contact to output of inverters (metal1 path) - # mid0: metal1 path must route over the read transistors (above drain of read transistor) - # mid1: continue metal1 path horizontally until at first read access gate contact - # mid2: route up or down to be level with inverter output - # endpoint at drain/source of inverter - midL0 = vector(left_gate_contact.x, self.read_nmos_left[k].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"]) - midL1 = vector(left_gate_contact0.x, self.read_nmos_left[0].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"]) - midL2 = vector(left_gate_contact0.x, self.cross_couple_upper_ypos) - left_inverter_offset = vector(self.inverter_nmos_left.get_pin("D").center().x, self.cross_couple_upper_ypos) - self.add_path("metal1", [left_gate_contact, midL0, midL1, midL2, left_inverter_offset]) - - midR0 = vector(right_gate_contact.x, self.read_nmos_right[k].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"]) - midR1 = vector(right_gate_contact0.x, self.read_nmos_right[k].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"]) - midR2 = vector(right_gate_contact0.x, self.cross_couple_upper_ypos) - right_inverter_offset = vector(self.inverter_nmos_right.get_pin("S").center().x, self.cross_couple_upper_ypos) - self.add_path("metal1", [right_gate_contact, midR0, midR1, midR2, right_inverter_offset]) - - def extend_well(self): - """ - Connects wells between ptx modules to avoid drc spacing issues. - Since the pwell of the read ports rise higher than the nwell of the inverters, - the well connections must be done piecewise to avoid pwell and nwell overlap. - """ - - # extend pwell to encompass entire nmos region of the cell up to the height of the inverter nmos well - offset = vector(self.leftmost_xpos, self.botmost_ypos) - well_height = -self.botmost_ypos + self.inverter_nmos.cell_well_height - drc["well_enclosure_active"] - self.add_rect(layer="pwell", - offset=offset, - width=self.width, - height=well_height) - - # extend pwell over read/write and write transistors to the - # height of the write transistor well (read/write and write - # transistors are the same height) - if(self.num_w_ports > 0): - # calculate the edge of the write transistor well closest to the center - left_write_well_xpos = self.write_nmos_left[0].offset.x + drc["well_enclosure_active"] - right_write_well_xpos = self.write_nmos_right[0].offset.x - self.write_nmos.active_height - drc["well_enclosure_active"] - else: - # calculate the edge of the read/write transistor well closest to the center - left_write_well_xpos = self.readwrite_nmos_left[0].offset.x + drc["well_enclosure_active"] - right_write_well_xpos = self.readwrite_nmos_right[0].offset.x - self.readwrite_nmos.active_height - drc["well_enclosure_active"] - - # calculate a width that will halt at the edge of the write transistors - write_well_width = -(self.leftmost_xpos - left_write_well_xpos) - write_well_height = self.write_nmos.cell_well_width - drc["well_enclosure_active"] - - offset = vector(left_write_well_xpos - write_well_width, 0) - self.add_rect(layer="pwell", - offset=offset, - width=write_well_width, - height=write_well_height) - - offset = vector(right_write_well_xpos, 0) - self.add_rect(layer="pwell", - offset=offset, - width=write_well_width, - height=write_well_height) - - # extend pwell over the read transistors to the height of the bitcell - if(self.num_r_ports > 0): - # calculate the edge of the read transistor well clostest to the center - left_read_well_xpos = self.read_nmos_left[0].offset.x + drc["well_enclosure_active"] - right_read_well_xpos = self.read_nmos_right[0].offset.x - self.read_nmos.active_height - drc["well_enclosure_active"] - - # calculate a width that will halt at the edge of the read transistors - read_well_width = -(self.leftmost_xpos - left_read_well_xpos) - read_well_height = self.topmost_ypos - - offset = vector(self.leftmost_xpos, 0) - self.add_rect(layer="pwell", - offset=offset, - width=read_well_width, - height=read_well_height) - - offset = vector(right_read_well_xpos, 0) - self.add_rect(layer="pwell", - offset=offset, - width=read_well_width, - height=read_well_height) - - # extend nwell to encompass inverter_pmos - # calculate offset of the left pmos well - inverter_well_xpos = -self.inverter_tile_width - drc["well_enclosure_active"] - inverter_well_ypos = self.inverter_nmos.active_height + self.inverter_gap - drc["well_enclosure_active"] - - # calculate width of the two combined nwells - # calculate height to encompass nimplant connected to vdd - well_width = 2*self.inverter_tile_width + 2*drc["well_enclosure_active"] - well_height = self.vdd_position.y - inverter_well_ypos + drc["well_enclosure_active"] + drc["minwidth_tx"] - - offset = [inverter_well_xpos,inverter_well_ypos] - self.add_rect(layer="nwell", - offset=offset, - width=well_width, - height=well_height) - - - # add well contacts - # connect pimplants to gnd - offset = vector(0, self.gnd_position.y + 0.5*contact.well.second_layer_width) - self.add_contact_center(layers=("active", "contact", "metal1"), - offset=offset, - rotate=90, - implant_type="p", - well_type="p") - - # connect nimplants to vdd - offset = vector(0, self.vdd_position.y + 0.5*drc["minwidth_metal1"]) - self.add_contact_center(layers=("active", "contact", "metal1"), - offset=offset, - rotate=90, - implant_type="n", - well_type="n") - - - def list_bitcell_pins(self, col, row): - """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ - bitcell_pins = [] - for port in range(self.total_ports): - bitcell_pins.append("bl{0}[{1}]".format(port,col)) - bitcell_pins.append("br{0}[{1}]".format(port,col)) - for port in range(self.total_ports): - bitcell_pins.append("wl{0}[{1}]".format(port,row)) - bitcell_pins.append("vdd") - bitcell_pins.append("gnd") - return bitcell_pins - - def list_all_wl_names(self): - """ Creates a list of all wordline pin names """ - wordline_names = self.rw_wl_names + self.w_wl_names + self.r_wl_names - return wordline_names - - def list_all_bitline_names(self): - """ Creates a list of all bitline pin names (both bl and br) """ - bitline_pins = [] - for port in range(self.total_ports): - bitline_pins.append("bl{0}".format(port)) - bitline_pins.append("br{0}".format(port)) - return bitline_pins - - def list_all_bl_names(self): - """ Creates a list of all bl pins names """ - bl_pins = self.rw_bl_names + self.w_bl_names + self.r_bl_names - return bl_pins - - def list_all_br_names(self): - """ Creates a list of all br pins names """ - br_pins = self.rw_br_names + self.w_br_names + self.r_br_names - return br_pins - - def list_read_bl_names(self): - """ Creates a list of bl pin names associated with read ports """ - bl_pins = self.rw_bl_names + self.r_bl_names - return bl_pins - - def list_read_br_names(self): - """ Creates a list of br pin names associated with read ports """ - br_pins = self.rw_br_names + self.r_br_names - return br_pins - - def list_write_bl_names(self): - """ Creates a list of bl pin names associated with write ports """ - bl_pins = self.rw_bl_names + self.w_bl_names - return bl_pins - - def list_write_br_names(self): - """ Creates a list of br pin names asscociated with write ports""" - br_pins = self.rw_br_names + self.w_br_names - return br_pins diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py new file mode 100644 index 00000000..9b6c59ff --- /dev/null +++ b/compiler/pgates/pbuf.py @@ -0,0 +1,144 @@ +import debug +from tech import drc +from math import log +from vector import vector +from globals import OPTS +from pinv import pinv +import pgate + +class pbuf(pgate.pgate): + """ + This is a simple buffer used for driving loads. + """ + from importlib import reload + c = reload(__import__(OPTS.bitcell)) + bitcell = getattr(c, OPTS.bitcell) + + unique_id = 1 + + def __init__(self, size=4, height=None, name=""): + + self.stage_effort = 4 + self.size = size + self.height = height + + if name=="": + name = "pbuf_{0}_{1}".format(self.size, pbuf.unique_id) + pbuf.unique_id += 1 + + pgate.pgate.__init__(self, name, height) + debug.info(1, "creating {0} with size of {1}".format(self.name,self.size)) + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_layout(self): + self.width = self.inv1.width + self.inv2.width + self.place_insts() + self.add_wires() + self.add_layout_pins() + + def add_pins(self): + self.add_pin("A") + self.add_pin("Z") + self.add_pin("vdd") + self.add_pin("gnd") + + def create_modules(self): + # Shield the cap, but have at least a stage effort of 4 + input_size = max(1,int(self.size/self.stage_effort)) + self.inv1 = pinv(size=input_size, height=self.height) + self.add_mod(self.inv1) + + self.inv2 = pinv(size=self.size, height=self.height) + self.add_mod(self.inv2) + + def create_insts(self): + self.inv1_inst=self.add_inst(name="buf_inv1", + mod=self.inv1) + self.connect_inst(["A", "zb_int", "vdd", "gnd"]) + + + self.inv2_inst=self.add_inst(name="buf_inv2", + mod=self.inv2) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add INV1 to the right + self.inv1_inst.place(vector(0,0)) + + # Add INV2 to the right + self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) + + + def add_wires(self): + # inv1 Z to inv2 A + z1_pin = self.inv1_inst.get_pin("Z") + a2_pin = self.inv2_inst.get_pin("A") + mid_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()]) + + + def add_layout_pins(self): + # Continous vdd rail along with label. + vdd_pin=self.inv1_inst.get_pin("vdd") + self.add_layout_pin(text="vdd", + layer="metal1", + offset=vdd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + # Continous gnd rail along with label. + gnd_pin=self.inv1_inst.get_pin("gnd") + self.add_layout_pin(text="gnd", + layer="metal1", + offset=gnd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + z_pin = self.inv2_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=z_pin.layer, + offset=z_pin.center(), + width=z_pin.width(), + height=z_pin.height()) + + a_pin = self.inv1_inst.get_pin("A") + self.add_layout_pin_rect_center(text="A", + layer=a_pin.layer, + offset=a_pin.center(), + width=a_pin.width(), + height=a_pin.height()) + + + + def analytical_delay(self, slew, load=0.0): + """ Calculate the analytical delay of DFF-> INV -> INV """ + inv1_delay = self.inv1.analytical_delay(slew=slew, load=self.inv2.input_load()) + inv2_delay = self.inv2.analytical_delay(slew=inv1_delay.slew, load=load) + return inv1_delay + inv2_delay + + def get_output_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv2.get_cin() + stage1 = self.inv1.get_effort_stage(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv2.get_effort_stage(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Returns the relative capacitance of the input""" + input_cin = self.inv1.get_cin() + return input_cin \ No newline at end of file diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py new file mode 100644 index 00000000..5da68032 --- /dev/null +++ b/compiler/pgates/pdriver.py @@ -0,0 +1,232 @@ +import debug +import pgate +import math +from tech import drc +from math import log +from vector import vector +from globals import OPTS +from pinv import pinv + +class pdriver(pgate.pgate): + """ + This instantiates an even or odd number of inverters sized for driving a load. + """ + unique_id = 1 + + def __init__(self, neg_polarity=False, fanout_size=8, size_list = [], height=None, name=""): + + self.stage_effort = 4 + self.height = height + self.neg_polarity = neg_polarity + self.size_list = size_list + self.fanout_size = fanout_size + + if len(self.size_list) > 0 and (self.fanout_size != 8 or self.neg_polarity): + debug.error("Cannot specify both size_list and neg_polarity or fanout_size.", -1) + + if name=="": + name = "pdriver_{}".format(pdriver.unique_id) + pdriver.unique_id += 1 + + pgate.pgate.__init__(self, name, height) + debug.info(1, "Creating {}".format(self.name)) + + self.compute_sizes() + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def compute_sizes(self): + # size_list specified + if len(self.size_list) > 0: + if not len(self.size_list) % 2: + neg_polarity = True + self.num_inv = len(self.size_list) + else: + # find the number of stages + #fanout_size is a unit inverter fanout, not a capacitance so c_in=1 + num_stages = max(1,int(round(log(self.fanout_size)/log(4)))) + + # find inv_num and compute sizes + if self.neg_polarity: + if (num_stages % 2 == 0): # if num_stages is even + self.diff_polarity(num_stages=num_stages) + else: # if num_stages is odd + self.same_polarity(num_stages=num_stages) + else: # positive polarity + if (num_stages % 2 == 0): + self.same_polarity(num_stages=num_stages) + else: + self.diff_polarity(num_stages=num_stages) + + + def same_polarity(self, num_stages): + self.calc_size_list = [] + self.num_inv = num_stages + # compute sizes + fanout_size_prev = self.fanout_size + for x in range(self.num_inv-1,-1,-1): + fanout_size_prev = int(round(fanout_size_prev/self.stage_effort)) + self.calc_size_list.append(fanout_size_prev) + + + def diff_polarity(self, num_stages): + self.calc_size_list = [] + # find which delay is smaller + if (num_stages > 1): + delay_below = ((num_stages-1)*(self.fanout_size**(1/num_stages-1))) + num_stages-1 + delay_above = ((num_stages+1)*(self.fanout_size**(1/num_stages+1))) + num_stages+1 + if (delay_above < delay_below): + # recompute stage_effort for this delay + self.num_inv = num_stages+1 + polarity_stage_effort = self.fanout_size**(1/self.num_inv) + else: + self.num_inv = num_stages-1 + polarity_stage_effort = self.fanout_size**(1/self.num_inv) + else: # num_stages is 1, can't go to 0 + self.num_inv = num_stages+1 + polarity_stage_effort = self.fanout_size**(1/self.num_inv) + + + # compute sizes + fanout_size_prev = self.fanout_size + for x in range(self.num_inv-1,-1,-1): + fanout_size_prev = int(round(fanout_size_prev/polarity_stage_effort)) + self.calc_size_list.append(fanout_size_prev) + + + def create_netlist(self): + inv_list = [] + + self.add_pins() + self.add_modules() + self.create_insts() + + def create_layout(self): + self.width = self.num_inv * self.inv_list[0].width + self.height = self.inv_list[0].height + + self.place_modules() + self.route_wires() + self.add_layout_pins() + + self.DRC_LVS() + + def add_pins(self): + self.add_pin("A") + self.add_pin("Z") + self.add_pin("vdd") + self.add_pin("gnd") + + def add_modules(self): + self.inv_list = [] + if len(self.size_list) > 0: # size list specified + for x in range(len(self.size_list)): + self.inv_list.append(pinv(size=self.size_list[x], height=self.height)) + self.add_mod(self.inv_list[x]) + else: # find inv sizes + for x in range(len(self.calc_size_list)): + self.inv_list.append(pinv(size=self.calc_size_list[x], height=self.height)) + self.add_mod(self.inv_list[x]) + + + def create_insts(self): + self.inv_inst_list = [] + for x in range(1,self.num_inv+1): + # Create first inverter + if x == 1: + zbx_int = "Zb{}_int".format(x); + self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), + mod=self.inv_list[x-1])) + if self.num_inv == 1: + self.connect_inst(["A", "Z", "vdd", "gnd"]) + else: + self.connect_inst(["A", zbx_int, "vdd", "gnd"]) + + # Create last inverter + elif x == self.num_inv: + zbn_int = "Zb{}_int".format(x-1); + self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), + mod=self.inv_list[x-1])) + self.connect_inst([zbn_int, "Z", "vdd", "gnd"]) + + # Create middle inverters + else: + zbx_int = "Zb{}_int".format(x-1); + zbn_int = "Zb{}_int".format(x); + self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), + mod=self.inv_list[x-1])) + self.connect_inst([zbx_int, zbn_int, "vdd", "gnd"]) + + + def place_modules(self): + # Add INV1 to the left + self.inv_inst_list[0].place(vector(0,0)) + + # Add inverters to the right of INV1 + for x in range(1,len(self.inv_inst_list)): + self.inv_inst_list[x].place(vector(self.inv_inst_list[x-1].rx(),0)) + + + def route_wires(self): + z_inst_list = [] + a_inst_list = [] + # inv_current Z to inv_next A + for x in range(0,len(self.inv_inst_list)-1): + z_inst_list.append(self.inv_inst_list[x].get_pin("Z")) + a_inst_list.append(self.inv_inst_list[x+1].get_pin("A")) + mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy()) + self.add_path("metal1", [z_inst_list[x].center(), mid_point, a_inst_list[x].center()]) + + + def add_layout_pins(self): + # Continous vdd rail along with label. + vdd_pin=self.inv_inst_list[0].get_pin("vdd") + self.add_layout_pin(text="vdd", + layer="metal1", + offset=vdd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + # Continous gnd rail along with label. + gnd_pin=self.inv_inst_list[0].get_pin("gnd") + self.add_layout_pin(text="gnd", + layer="metal1", + offset=gnd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + z_pin = self.inv_inst_list[len(self.inv_inst_list)-1].get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=z_pin.layer, + offset=z_pin.center(), + width = z_pin.width(), + height = z_pin.height()) + + a_pin = self.inv_inst_list[0].get_pin("A") + self.add_layout_pin_rect_center(text="A", + layer=a_pin.layer, + offset=a_pin.center(), + width = a_pin.width(), + height = a_pin.height()) + + def analytical_delay(self, slew, load=0.0): + """Calculate the analytical delay of INV1 -> ... -> INVn""" + delay = 0; + if len(self.inv_inst_list) == 1: + delay = self.inv_inst_list[x].analytical_delay(slew=slew); + else: + for x in range(len(self.inv_inst_list-1)): + load_next = 0.0 + for n in range(x,len(self.inv_inst_list+1)): + load_next += self.inv_inst_list[x+1] + if x == 1: + delay += self.inv_inst_list[x].analytical_delay(slew=slew, + load=load_next) + else: + delay += self.inv_inst_list[x+1].analytical_delay(slew=delay.slew, + load=load_next) + return delay + + diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index ec3bed0c..fc839270 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -1,7 +1,7 @@ import contact import design import debug -from tech import drc, parameter, spice, info +from tech import drc, parameter, spice from ptx import ptx from vector import vector from globals import OPTS @@ -110,7 +110,7 @@ class pgate(design.design): max_y_offset = self.height + 0.5*self.m1_width self.nwell_position = middle_position nwell_height = max_y_offset - middle_position.y - if info["has_nwell"]: + if drc("has_nwell"): self.add_rect(layer="nwell", offset=middle_position, width=self.well_width, @@ -122,7 +122,7 @@ class pgate(design.design): pwell_position = vector(0,-0.5*self.m1_width) pwell_height = middle_position.y-pwell_position.y - if info["has_pwell"]: + if drc("has_pwell"): self.add_rect(layer="pwell", offset=pwell_position, width=self.well_width, @@ -138,7 +138,7 @@ class pgate(design.design): layer_stack = ("active", "contact", "metal1") # To the right a spacing away from the pmos right active edge - contact_xoffset = pmos_pos.x + pmos.active_width + drc["active_to_body_active"] + contact_xoffset = pmos_pos.x + pmos.active_width + drc("active_to_body_active") # Must be at least an well enclosure of active down from the top of the well # OR align the active with the top of PMOS active. max_y_offset = self.height + 0.5*self.m1_width @@ -185,7 +185,7 @@ class pgate(design.design): pwell_position = vector(0,-0.5*self.m1_width) # To the right a spacing away from the nmos right active edge - contact_xoffset = nmos_pos.x + nmos.active_width + drc["active_to_body_active"] + contact_xoffset = nmos_pos.x + nmos.active_width + drc("active_to_body_active") # Must be at least an well enclosure of active up from the bottom of the well contact_yoffset = max(nmos_pos.y, self.well_enclose_active - nmos.active_contact.first_layer_height/2) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 8b3d1716..31682360 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -1,12 +1,13 @@ import contact import pgate import debug -from tech import drc, parameter, spice, info +from tech import drc, parameter, spice from ptx import ptx from vector import vector from math import ceil from globals import OPTS from utils import round_to_grid +import logical_effort class pinv(pgate.pgate): """ @@ -29,7 +30,8 @@ class pinv(pgate.pgate): pinv.unique_id += 1 pgate.pgate.__init__(self, name, height) debug.info(2, "create pinv structure {0} with size of {1}".format(name, size)) - + + self.size = size self.nmos_size = size self.pmos_size = beta*size self.beta = beta @@ -76,8 +78,8 @@ class pinv(pgate.pgate): # This may make the result differ when the layout is created... if OPTS.netlist_only: self.tx_mults = 1 - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") return # Do a quick sanity check and bail if unlikely feasible height @@ -85,16 +87,16 @@ class pinv(pgate.pgate): # Assume we need 3 metal 1 pitches (2 power rails, one between the tx for the drain) # plus the tx height nmos = ptx(tx_type="nmos") - pmos = ptx(width=drc["minwidth_tx"], tx_type="pmos") + pmos = ptx(width=drc("minwidth_tx"), tx_type="pmos") tx_height = nmos.poly_height + pmos.poly_height # rotated m1 pitch or poly to active spacing min_channel = max(contact.poly.width + self.m1_space, - contact.poly.width + 2*drc["poly_to_active"]) + contact.poly.width + 2*drc("poly_to_active")) # This is the extra space needed to ensure DRC rules to the active contacts extra_contact_space = max(-nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc["poly_extend_active"], self.poly_space) + drc("poly_extend_active"), self.poly_space) total_height = tx_height + min_channel + 2*self.top_bottom_space debug.check(self.height> total_height,"Cell height {0} too small for simple min height {1}.".format(self.height,total_height)) @@ -103,16 +105,16 @@ class pinv(pgate.pgate): # Divide the height in half. Could divide proportional to beta, but this makes # connecting wells of multiple cells easier. # Subtract the poly space under the rail of the tx - 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"] + 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:.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"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") nmos_required_mults = max(int(ceil(self.nmos_width/nmos_height_available)),1) pmos_required_mults = max(int(ceil(self.pmos_width/pmos_height_available)),1) # The mults must be the same for easy connection of poly @@ -124,9 +126,9 @@ class pinv(pgate.pgate): # We also need to round the width to the grid or we will end up with LVS property # mismatch errors when fingers are not a grid length and get rounded in the offset geometry. self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults) - debug.check(self.nmos_width>=drc["minwidth_tx"],"Cannot finger NMOS transistors to fit cell height.") + debug.check(self.nmos_width>=drc("minwidth_tx"),"Cannot finger NMOS transistors to fit cell height.") self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults) - debug.check(self.pmos_width>=drc["minwidth_tx"],"Cannot finger PMOS transistors to fit cell height.") + debug.check(self.pmos_width>=drc("minwidth_tx"),"Cannot finger PMOS transistors to fit cell height.") def setup_layout_constants(self): @@ -137,7 +139,7 @@ class pinv(pgate.pgate): # the well width is determined the multi-finger PMOS device width plus # the well contact width and half well enclosure on both sides self.well_width = self.pmos.active_width + self.pmos.active_contact.width \ - + drc["active_to_body_active"] + 2*drc["well_enclosure_active"] + + drc("active_to_body_active") + 2*drc("well_enclosure_active") self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -281,3 +283,14 @@ class pinv(pgate.pgate): c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff transition_prob = spice["inv_transition_prob"] return transition_prob*(c_load + c_para) + + def get_cin(self): + """Return the capacitance of the gate connection in generic capacitive units relative to the minimum width of a transistor""" + return self.nmos_size + self.pmos_size + + def get_effort_stage(self, cout, inp_is_rise=True): + """Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. + """ + parasitic_delay = 1 + return logical_effort.logical_effort(self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index 328836dc..13c376cf 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -94,16 +94,16 @@ class pinvbuf(design.design): self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) def place_modules(self): - # Add INV1 to the right (capacitance shield) + # Add INV1 to the left (capacitance shield) self.inv1_inst.place(vector(0,0)) - # Add INV2 to the right + # Add INV2 to the right of INV1 self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) - # Add INV3 to the right + # Add INV3 to the right of INV2 self.inv3_inst.place(vector(self.inv2_inst.rx(),0)) - # Add INV4 to the bottom + # Add INV4 flipped to the bottom aligned with INV2 self.inv4_inst.place(offset=vector(self.inv2_inst.rx(),2*self.inv2.height), mirror = "MX") @@ -187,3 +187,34 @@ class pinvbuf(design.design): inv2_delay = self.inv2.analytical_delay(slew=inv1_delay.slew, load=load) return inv1_delay + inv2_delay + def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the clk -> clk_buf path""" + stage_effort_list = [] + stage1_cout = self.inv1.get_cin() + self.inv2.get_cin() + stage1 = self.inv.get_effort_stage(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv2.get_effort_stage(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def determine_clk_buf_bar_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the clk -> clk_buf path""" + #After (almost) every stage, the direction of the signal inverts. + stage_effort_list = [] + stage1_cout = self.inv1.get_cin() + self.inv2.get_cin() + stage1 = self.inv.get_effort_stage(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage_effort_list[-1].is_rise + + stage2_cout = self.inv2.get_cin() + stage2 = self.inv1.get_effort_stage(stage2_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + last_stage_is_rise = stage_effort_list[-1].is_rise + + stage3 = self.inv2.get_effort_stage(external_cout, last_stage_is_rise) + stage_effort_list.append(stage3) + + return stage_effort_list \ No newline at end of file diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 14923a84..e767b87e 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -5,6 +5,7 @@ from tech import drc, parameter, spice from ptx import ptx from vector import vector from globals import OPTS +import logical_effort class pnand2(pgate.pgate): """ @@ -21,10 +22,11 @@ class pnand2(pgate.pgate): pgate.pgate.__init__(self, name, height) debug.info(2, "create pnand2 structure {0} with size of {1}".format(name, size)) + self.size = size self.nmos_size = 2*size self.pmos_size = parameter["beta"]*size - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnand2 is only supported now.") @@ -33,7 +35,6 @@ class pnand2(pgate.pgate): self.create_netlist() if not OPTS.netlist_only: self.create_layout() - #self.DRC_LVS() def create_netlist(self): @@ -91,7 +92,7 @@ class pnand2(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 2*self.pmos.active_width + contact.active.width \ - + 2*drc["active_to_body_active"] + 2*drc["well_enclosure_active"] + + 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -100,7 +101,7 @@ class pnand2(pgate.pgate): extra_contact_space = max(-self.nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc["poly_extend_active"], self.poly_space) + drc("poly_extend_active"), self.poly_space) def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ @@ -192,26 +193,33 @@ class pnand2(pgate.pgate): """ Route the Z output """ # PMOS1 drain pmos_pin = self.pmos1_inst.get_pin("D") + top_pin_offset = pmos_pin.center() # NMOS2 drain - nmos_pin = self.nmos2_inst.get_pin("D") + nmos_pin = self.nmos2_inst.get_pin("D") + bottom_pin_offset = nmos_pin.center() + # Output pin - mid_offset = vector(nmos_pin.center().x,self.inputA_yoffset) + out_offset = vector(nmos_pin.center().x + self.m1_pitch,self.inputA_yoffset) + + # Midpoints of the L routes go horizontal first then vertical + mid1_offset = vector(out_offset.x, top_pin_offset.y) + mid2_offset = vector(out_offset.x, bottom_pin_offset.y) self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=pmos_pin.center()) self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=nmos_pin.center()) self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=mid_offset, + offset=out_offset, rotate=90) # PMOS1 to mid-drain to NMOS2 drain - self.add_path("metal2",[pmos_pin.bc(), mid_offset, nmos_pin.uc()]) + self.add_path("metal2",[top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset]) # This extends the output to the edge of the cell self.add_layout_pin_rect_center(text="Z", layer="metal1", - offset=mid_offset, + offset=out_offset, width=contact.m1m2.first_layer_height, height=contact.m1m2.first_layer_width) @@ -242,3 +250,14 @@ class pnand2(pgate.pgate): c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff transition_prob = spice["nand2_transition_prob"] return transition_prob*(c_load + c_para) + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nmos_size+self.pmos_size + + def get_effort_stage(self, cout, inp_is_rise=True): + """Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. + """ + parasitic_delay = 2 + return logical_effort.logical_effort(self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) \ No newline at end of file diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 75887ed3..4dab5264 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -23,10 +23,11 @@ class pnand3(pgate.pgate): # We have trouble pitch matching a 3x sizes to the bitcell... # If we relax this, we could size this better. + self.size = size self.nmos_size = 2*size self.pmos_size = parameter["beta"]*size - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnand3 is only supported now.") @@ -83,7 +84,7 @@ class pnand3(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 3*self.pmos.active_width + self.pmos.active_contact.width \ - + 2*drc["active_to_body_active"] + 2*drc["well_enclosure_active"] \ + + 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") \ - self.overlap_offset.x self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -96,7 +97,7 @@ class pnand3(pgate.pgate): extra_contact_space = max(-nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc["poly_extend_active"], self.poly_space) + drc("poly_extend_active"), self.poly_space) def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ @@ -191,7 +192,7 @@ class pnand3(pgate.pgate): metal_spacing = max(self.m1_space + self.m1_width, self.m2_space + self.m2_width, self.m1_space + 0.5*contact.poly.width + 0.5*self.m1_width) - active_spacing = max(self.m1_space, 0.5*contact.poly.first_layer_width + drc["poly_to_active"]) + active_spacing = max(self.m1_space, 0.5*contact.poly.first_layer_width + drc("poly_to_active")) inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing self.route_input_gate(self.pmos3_inst, self.nmos3_inst, inputC_yoffset, "C", position="center") @@ -261,3 +262,14 @@ class pnand3(pgate.pgate): c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff transition_prob = spice["nand3_transition_prob"] return transition_prob*(c_load + c_para) + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nmos_size+self.pmos_size + + def get_effort_stage(self, cout, inp_is_rise=True): + """Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. + """ + parasitic_delay = 3 + return logical_effort.logical_effort(self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) \ No newline at end of file diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 87196342..65aaf7f8 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -24,8 +24,8 @@ class pnor2(pgate.pgate): self.nmos_size = size # We will just make this 1.5 times for now. NORs are not ideal anyhow. self.pmos_size = 1.5*parameter["beta"]*size - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnor2 is only supported now.") @@ -92,7 +92,7 @@ class pnor2(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 2*self.pmos.active_width + self.pmos.active_contact.width \ - + 2*drc["active_to_body_active"] + 2*drc["well_enclosure_active"] + + 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -101,7 +101,7 @@ class pnor2(pgate.pgate): extra_contact_space = max(-self.nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc["poly_extend_active"], self.poly_space) + drc("poly_extend_active"), self.poly_space) def add_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 3ddca616..a1422cb2 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -51,10 +51,12 @@ class precharge(pgate.pgate): self.DRC_LVS() def add_pins(self): - self.add_pin_list(["bl", "br", "en", "vdd"]) + self.add_pin_list(["bl", "br", "en_bar", "vdd"]) def add_ptx(self): - """Initializes the upper and lower pmos""" + """ + Initializes the upper and lower pmos + """ self.pmos = ptx(width=self.ptx_width, tx_type="pmos") self.add_mod(self.pmos) @@ -63,56 +65,58 @@ class precharge(pgate.pgate): def route_vdd_rail(self): + """ + Adds a vdd rail at the top of the cell + """ - """Adds a vdd rail at the top of the cell""" - # adds the rail across the width of the cell - vdd_position = vector(0, self.height - self.m1_width) - self.add_rect(layer="metal1", - offset=vdd_position, - width=self.width, - height=self.m1_width) + # Adds the rail across the width of the cell + vdd_position = vector(0.5*self.width, self.height) + self.add_rect_center(layer="metal1", + offset=vdd_position, + width=self.width, + height=self.m1_width) pmos_pin = self.upper_pmos2_inst.get_pin("S") # center of vdd rail - vdd_pos = vector(pmos_pin.cx(), vdd_position.y + 0.5*self.m1_width) - self.add_path("metal1", [pmos_pin.uc(), vdd_pos]) + pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) + self.add_path("metal1", [pmos_pin.uc(), pmos_vdd_pos]) - # Add the M1->M2->M3 stack at the left edge - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=vdd_pos.scale(0,1)) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=vdd_pos.scale(0,1)) - self.add_layout_pin_rect_center(text="vdd", - layer="metal3", - offset=vdd_pos.scale(0,1)) + # Add vdd pin above the transistor + self.add_power_pin("vdd", pmos_pin.center(), rotate=0) def create_ptx(self): - """Create both the upper_pmos and lower_pmos to the module""" + """ + Create both the upper_pmos and lower_pmos to the module + """ self.lower_pmos_inst=self.add_inst(name="lower_pmos", mod=self.pmos) - self.connect_inst(["bl", "en", "br", "vdd"]) + self.connect_inst(["bl", "en_bar", "br", "vdd"]) self.upper_pmos1_inst=self.add_inst(name="upper_pmos1", mod=self.pmos) - self.connect_inst(["bl", "en", "vdd", "vdd"]) + self.connect_inst(["bl", "en_bar", "vdd", "vdd"]) self.upper_pmos2_inst=self.add_inst(name="upper_pmos2", mod=self.pmos) - self.connect_inst(["br", "en", "vdd", "vdd"]) + self.connect_inst(["br", "en_bar", "vdd", "vdd"]) def place_ptx(self): - """Place both the upper_pmos and lower_pmos to the module""" + """ + Place both the upper_pmos and lower_pmos to the module + """ # Compute the other pmos2 location, but determining offset to overlap the # source and drain pins - self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() + overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() + # This is how much the contact is placed inside the ptx active + contact_xdiff = self.pmos.get_pin("S").lx() # adds the lower pmos to layout - #base = vector(self.width - 2*self.pmos.width + self.overlap_offset.x, 0) - self.lower_pmos_position = vector(self.bitcell.get_pin(self.bitcell_bl).lx(), + bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx() + self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, self.well_enclose_active), self.pmos.active_offset.y) self.lower_pmos_inst.place(self.lower_pmos_position) @@ -121,11 +125,13 @@ class precharge(pgate.pgate): self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff) self.upper_pmos1_inst.place(self.upper_pmos1_pos) - upper_pmos2_pos = self.upper_pmos1_pos + self.overlap_offset + upper_pmos2_pos = self.upper_pmos1_pos + overlap_offset self.upper_pmos2_inst.place(upper_pmos2_pos) def connect_poly(self): - """Connects the upper and lower pmos together""" + """ + Connects the upper and lower pmos together + """ offset = self.lower_pmos_inst.get_pin("G").ll() # connects the top and bottom pmos' gates together @@ -144,7 +150,10 @@ class precharge(pgate.pgate): height=self.poly_width) def route_en(self): - """Adds the en input rail, en contact/vias, and connects to the pmos""" + """ + Adds the en input rail, en contact/vias, and connects to the pmos + """ + # adds the en contact to connect the gates to the en rail on metal1 offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space) self.add_contact_center(layers=("poly", "contact", "metal1"), @@ -152,25 +161,29 @@ class precharge(pgate.pgate): rotate=90) # adds the en rail on metal1 - self.add_layout_pin_segment_center(text="en", + self.add_layout_pin_segment_center(text="en_bar", layer="metal1", start=offset.scale(0,1), end=offset.scale(0,1)+vector(self.width,0)) def place_nwell_and_contact(self): - """Adds a nwell tap to connect to the vdd rail""" + """ + Adds a nwell tap to connect to the vdd rail + """ + # adds the contact from active to metal1 well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \ - + vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc["well_extend_active"]) + + vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc("well_extend_active")) self.add_contact_center(layers=("active", "contact", "metal1"), offset=well_contact_pos, implant_type="n", well_type="n") + # leave an extra pitch for the height + self.height = well_contact_pos.y + contact.well.height + self.m1_pitch - self.height = well_contact_pos.y + contact.well.height - + # nwell should span the whole design since it is pmos only self.add_rect(layer="nwell", offset=vector(0,0), width=self.width, @@ -178,58 +191,81 @@ class precharge(pgate.pgate): def route_bitlines(self): - """Adds both bit-line and bit-line-bar to the module""" + """ + Adds both bit-line and bit-line-bar to the module + """ + # adds the BL on metal 2 offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0) - self.add_layout_pin(text="bl", - layer="metal2", - offset=offset, - width=drc['minwidth_metal2'], - height=self.height) + self.bl_pin = self.add_layout_pin(text="bl", + layer="metal2", + offset=offset, + width=drc("minwidth_metal2"), + height=self.height) # adds the BR on metal 2 offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(),0) - vector(0.5 * self.m2_width,0) - self.add_layout_pin(text="br", - layer="metal2", - offset=offset, - width=drc['minwidth_metal2'], - height=self.height) + self.br_pin = self.add_layout_pin(text="br", + layer="metal2", + offset=offset, + width=drc("minwidth_metal2"), + height=self.height) def connect_to_bitlines(self): + """ + Connect the bitlines to the devices + """ self.add_bitline_contacts() - self.connect_pmos(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl")) - self.connect_pmos(self.lower_pmos_inst.get_pin("D"),self.get_pin("br")) - self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl")) - self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br")) + self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl")) + self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl")) + self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),self.get_pin("br")) + self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br")) def add_bitline_contacts(self): - """Adds contacts/via from metal1 to metal2 for bit-lines""" + """ + Adds contacts/via from metal1 to metal2 for bit-lines + """ stack=("metal1", "via1", "metal2") - pos = self.lower_pmos_inst.get_pin("S").center() - self.add_contact_center(layers=stack, - offset=pos) - pos = self.lower_pmos_inst.get_pin("D").center() - self.add_contact_center(layers=stack, - offset=pos) - pos = self.upper_pmos1_inst.get_pin("S").center() - self.add_contact_center(layers=stack, - offset=pos) - pos = self.upper_pmos2_inst.get_pin("D").center() - self.add_contact_center(layers=stack, - offset=pos) - - def connect_pmos(self, pmos_pin, bit_pin): - """ Connect pmos pin to bitline pin """ - - ll_pos = vector(min(pmos_pin.lx(),bit_pin.lx()), pmos_pin.by()) - ur_pos = vector(max(pmos_pin.rx(),bit_pin.rx()), pmos_pin.uy()) - - width = ur_pos.x-ll_pos.x - height = ur_pos.y-ll_pos.y - self.add_rect(layer="metal2", - offset=ll_pos, - width=width, - height=height) + upper_pin = self.upper_pmos1_inst.get_pin("S") + lower_pin = self.lower_pmos_inst.get_pin("S") + # BL goes up to M2 at the transistor + self.bl_contact=self.add_contact_center(layers=stack, + offset=upper_pin.center()) + self.add_contact_center(layers=stack, + offset=lower_pin.center()) + + # BR routes over on M1 first + self.add_contact_center(layers=stack, + offset = vector(self.br_pin.cx(), upper_pin.cy())) + self.add_contact_center(layers=stack, + offset = vector(self.br_pin.cx(), lower_pin.cy())) + + def connect_pmos_m1(self, pmos_pin, bit_pin): + """ + Connect a pmos pin to bitline pin + """ + + left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) + right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) + + self.add_path("metal1", [ left_pos, right_pos] ) + + def connect_pmos_m2(self, pmos_pin, bit_pin): + """ + Connect a pmos pin to bitline pin + """ + + left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) + right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) + + self.add_path("metal2", [ left_pos, right_pos], self.bl_contact.height) + + def get_en_cin(self): + """Get the relative capacitance of the enable in the precharge cell""" + #The enable connect to three pmos gates. They all use the same size pmos. + pmos_cin = self.pmos.get_cin() + return 3*pmos_cin + \ No newline at end of file diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index e7fcd5f4..82c79aed 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -1,6 +1,6 @@ import design import debug -from tech import drc, info, spice +from tech import drc, spice from vector import vector from contact import contact from globals import OPTS @@ -15,7 +15,7 @@ class ptx(design.design): you to connect the fingered gates and active for parallel devices. """ - def __init__(self, width=drc["minwidth_tx"], mults=1, tx_type="nmos", connect_active=False, connect_poly=False, num_contacts=None): + def __init__(self, width=drc("minwidth_tx"), mults=1, tx_type="nmos", connect_active=False, connect_poly=False, num_contacts=None): # We need to keep unique names because outputting to GDSII # will use the last record with a given name. I.e., you will # over-write a design in GDS if one has and the other doesn't @@ -41,8 +41,9 @@ class ptx(design.design): self.num_contacts = num_contacts self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() + # We must always create ptx layout for pbitcell + # some transistor sizes in other netlist depend on pbitcell + self.create_layout() @@ -65,14 +66,14 @@ class ptx(design.design): # self.spice.append("\n.SUBCKT {0} {1}".format(self.name, # " ".join(self.pins))) # Just make a guess since these will actually be decided in the layout later. - area_sd = 2.5*drc["minwidth_poly"]*self.tx_width - perimeter_sd = 2*drc["minwidth_poly"] + 2*self.tx_width - self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4}u ps={4}u as={5}p ad={5}p".format(spice[self.tx_type], - self.mults, - self.tx_width, - drc["minwidth_poly"], - perimeter_sd, - area_sd) + area_sd = 2.5*drc("minwidth_poly")*self.tx_width + perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width + self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4:.2f}u ps={4:.2f}u as={5:.2f}p ad={5:.2f}p".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly"), + perimeter_sd, + area_sd) self.spice.append("\n* ptx " + self.spice_device) # self.spice.append(".ENDS {0}".format(self.name)) @@ -108,7 +109,7 @@ class ptx(design.design): self.contact_pitch = 2*self.contact_to_gate + self.contact_width + self.poly_width # The enclosure of an active contact. Not sure about second term. - active_enclose_contact = max(drc["active_enclosure_contact"], + active_enclose_contact = max(drc("active_enclosure_contact"), (self.active_width - self.contact_width)/2) # This is the distance from the edge of poly to the contacted end of active self.end_to_poly = active_enclose_contact + self.contact_width + self.contact_to_gate @@ -128,7 +129,7 @@ class ptx(design.design): self.active_offset = vector([self.well_enclose_active]*2) # Well enclosure of active, ensure minwidth as well - if info["has_{}well".format(self.well_type)]: + if drc("has_{}well".format(self.well_type)): self.cell_well_width = max(self.active_width + 2*self.well_enclose_active, self.well_width) self.cell_well_height = max(self.tx_width + 2*self.well_enclose_active, @@ -150,9 +151,9 @@ class ptx(design.design): # Min area results are just flagged for now. - debug.check(self.active_width*self.active_height>=drc["minarea_active"],"Minimum active area violated.") + debug.check(self.active_width*self.active_height>=drc("minarea_active"),"Minimum active area violated.") # We do not want to increase the poly dimensions to fix an area problem as it would cause an LVS issue. - debug.check(self.poly_width*self.poly_height>=drc["minarea_poly"],"Minimum poly area violated.") + debug.check(self.poly_width*self.poly_height>=drc("minarea_poly"),"Minimum poly area violated.") def connect_fingered_poly(self, poly_positions): """ @@ -180,7 +181,7 @@ class ptx(design.design): layer="poly", offset=poly_offset, width=poly_width, - height=drc["minwidth_poly"]) + height=drc("minwidth_poly")) def connect_fingered_active(self, drain_positions, source_positions): @@ -268,7 +269,7 @@ class ptx(design.design): height=self.active_height) # If the implant must enclose the active, shift offset # and increase width/height - enclose_width = drc["implant_enclosure_active"] + enclose_width = drc("implant_enclosure_active") enclose_offset = [enclose_width]*2 self.add_rect(layer="{}implant".format(self.implant_type), offset=self.active_offset - enclose_offset, @@ -279,7 +280,7 @@ class ptx(design.design): """ Add an (optional) well and implant for the type of transistor. """ - if info["has_{}well".format(self.well_type)]: + if drc("has_{}well".format(self.well_type)): self.add_rect(layer="{}well".format(self.well_type), offset=(0,0), width=self.cell_well_width, @@ -352,4 +353,6 @@ class ptx(design.design): if self.connect_active: self.connect_fingered_active(drain_positions, source_positions) - + def get_cin(self): + """Returns the relative gate cin of the tx""" + return self.tx_width/drc("minwidth_tx") \ No newline at end of file diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 0e1cd88f..197164f3 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -1,6 +1,6 @@ import design import debug -from tech import drc, info +from tech import drc from vector import vector import contact from ptx import ptx @@ -9,18 +9,21 @@ from globals import OPTS class single_level_column_mux(design.design): """ This module implements the columnmux bitline cell used in the design. - Creates a single columnmux cell. + Creates a single columnmux cell with the given integer size relative + to minimum size. Default is 8x. Per Samira and Hodges-Jackson book: + Column-mux transistors driven by the decoder must be sized for optimal speed """ + # This is needed for different bitline spacings unique_id = 1 - def __init__(self, tx_size, bitcell_bl="bl", bitcell_br="br"): - name="single_level_column_mux_{}_no{}".format(tx_size,single_level_column_mux.unique_id) + def __init__(self, tx_size=8, bitcell_bl="bl", bitcell_br="br"): + self.tx_size = int(tx_size) + name="single_level_column_mux_{}_{}".format(self.tx_size,single_level_column_mux.unique_id) single_level_column_mux.unique_id += 1 design.design.__init__(self, name) debug.info(2, "create single column mux cell: {0}".format(name)) - self.tx_size = tx_size self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br @@ -52,7 +55,7 @@ class single_level_column_mux(design.design): self.bitcell = self.mod_bitcell() # Adds nmos_lower,nmos_upper to the module - self.ptx_width = self.tx_size * drc["minwidth_tx"] + self.ptx_width = self.tx_size*drc("minwidth_tx") self.nmos = ptx(width=self.ptx_width) self.add_mod(self.nmos) @@ -144,8 +147,8 @@ class single_level_column_mux(design.design): # bl_out -> nmos_upper/S on metal2 self.add_path("metal1",[bl_pin.ll(), vector(nmos_upper_d_pin.cx(),bl_pin.by()), nmos_upper_d_pin.center()]) # halfway up, move over - mid1 = bl_out_pin.uc().scale(1,0.5)+nmos_upper_s_pin.bc().scale(0,0.5) - mid2 = bl_out_pin.uc().scale(0,0.5)+nmos_upper_s_pin.bc().scale(1,0.5) + mid1 = bl_out_pin.uc().scale(1,0.4)+nmos_upper_s_pin.bc().scale(0,0.4) + mid2 = bl_out_pin.uc().scale(0,0.4)+nmos_upper_s_pin.bc().scale(1,0.4) self.add_path("metal2",[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()]) # br -> nmos_lower/D on metal2 @@ -164,7 +167,7 @@ class single_level_column_mux(design.design): """ # Add it to the right, aligned in between the two tx - active_pos = vector(self.bitcell.width,self.nmos_upper.by()) + active_pos = vector(self.bitcell.width,self.nmos_upper.by() - 0.5*self.poly_space) active_via = self.add_via_center(layers=("active", "contact", "metal1"), offset=active_pos, implant_type="p", diff --git a/compiler/profile_stats.py b/compiler/profile_stats.py new file mode 100755 index 00000000..d3c33801 --- /dev/null +++ b/compiler/profile_stats.py @@ -0,0 +1,5 @@ +import pstats +p = pstats.Stats('profile.dat') +p.strip_dirs() +p.sort_stats('cumulative') +p.print_stats(50) diff --git a/compiler/router/direction.py b/compiler/router/direction.py new file mode 100644 index 00000000..8a6681a7 --- /dev/null +++ b/compiler/router/direction.py @@ -0,0 +1,67 @@ +from enum import Enum +from vector3d import vector3d + +class direction(Enum): + NORTH = 1 + SOUTH = 2 + EAST = 3 + WEST = 4 + UP = 5 + DOWN = 6 + NORTHEAST = 7 + NORTHWEST = 8 + SOUTHEAST = 9 + SOUTHWEST = 10 + + + def get_offset(direct): + """ + Returns the vector offset for a given direction. + """ + if direct==direction.NORTH: + offset = vector3d(0,1,0) + elif direct==direction.SOUTH: + offset = vector3d(0,-1,0) + elif direct==direction.EAST: + offset = vector3d(1,0,0) + elif direct==direction.WEST: + offset = vector3d(-1,0,0) + elif direct==direction.UP: + offset = vector3d(0,0,1) + elif direct==direction.DOWN: + offset = vector3d(0,0,-1) + elif direct==direction.NORTHEAST: + offset = vector3d(1,1,0) + elif direct==direction.NORTHWEST: + offset = vector3d(-1,1,0) + elif direct==direction.SOUTHEAST: + offset = vector3d(1,-1,0) + elif direct==direction.SOUTHWEST: + offset = vector3d(-1,-1,0) + else: + debug.error("Invalid direction {}".format(direct)) + + return offset + + def cardinal_directions(up_down_too=False): + temp_dirs = [direction.NORTH, direction.EAST, direction.SOUTH, direction.WEST] + if up_down_too: + temp_dirs.extend([direction.UP, direction.DOWN]) + return temp_dirs + + def cardinal_offsets(up_down_too=False): + return [direction.get_offset(d) for d in direction.cardinal_directions(up_down_too)] + + def all_directions(): + return [direction.NORTH, direction.EAST, direction.SOUTH, direction.WEST, + direction.NORTHEAST, direction.NORTHWEST, direction.SOUTHEAST, direction.SOUTHWEST] + + def all_offsets(): + return [direction.get_offset(d) for d in direction.all_directions()] + + def all_neighbors(cell): + return [cell+x for x in direction.all_offsets()] + + def cardinal_neighbors(cell): + return [cell+x for x in direction.cardinal_offsets()] + diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 8eb063cf..b43c36b2 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -1,95 +1,138 @@ import numpy as np import string -from itertools import tee import debug from vector3d import vector3d -from cell import cell -import os +from grid_cell import grid_cell class grid: """ A two layer routing map. Each cell can be blocked in the vertical or horizontal layer. """ + # costs are relative to a unit grid + # non-preferred cost allows an off-direction jog of 1 grid + # rather than 2 vias + preferred direction (cost 5) + VIA_COST = 2 + NONPREFERRED_COST = 4 + PREFERRED_COST = 1 - def __init__(self): + + def __init__(self, ll, ur, track_width): """ Initialize the map and define the costs. """ - # costs are relative to a unit grid - # non-preferred cost allows an off-direction jog of 1 grid - # rather than 2 vias + preferred direction (cost 5) - self.VIA_COST = 2 - self.NONPREFERRED_COST = 4 - self.PREFERRED_COST = 1 + # list of the source/target grid coordinates + self.source = [] + self.target = [] + self.track_width = track_width + self.track_widths = [self.track_width, self.track_width, 1.0] + self.track_factor = [1/self.track_width, 1/self.track_width, 1.0] + + # The bounds are in grids for this + # This is really lower left bottom layer and upper right top layer in 3D. + self.ll = vector3d(ll.x,ll.y,0).scale(self.track_factor).round() + self.ur = vector3d(ur.x,ur.y,1).scale(self.track_factor).round() + # let's leave the map sparse, cells are created on demand to reduce memory self.map={} - def set_blocked(self,n): - self.add_map(n) - self.map[n].blocked=True + def add_all_grids(self): + for x in range(self.ll.x, self.ur.x, 1): + for y in range(self.ll.y, self.ur.y, 1): + self.add_map(vector3d(x,y,0)) + self.add_map(vector3d(x,y,1)) + + def set_blocked(self,n,value=True): + if isinstance(n, (list,tuple,set,frozenset)): + for item in n: + self.set_blocked(item,value) + else: + self.add_map(n) + self.map[n].blocked=value def is_blocked(self,n): - self.add_map(n) - return self.map[n].blocked + if isinstance(n, (list,tuple,set,frozenset)): + for item in n: + if self.is_blocked(item): + return True + else: + return False + else: + self.add_map(n) + return self.map[n].blocked - def add_blockage_shape(self,ll,ur,z): - debug.info(3,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) - block_list = [] - for x in range(int(ll[0]),int(ur[0])+1): - for y in range(int(ll[1]),int(ur[1])+1): - block_list.append(vector3d(x,y,z)) + def set_path(self,n,value=True): + if isinstance(n, (list,tuple,set,frozenset)): + for item in n: + self.set_path(item,value) + else: + self.add_map(n) + self.map[n].path=value - self.add_blockage(block_list) + def clear_blockages(self): + self.set_blocked(set(self.map.keys()),False) + + def set_source(self,n,value=True): + if isinstance(n, (list,tuple,set,frozenset)): + for item in n: + self.set_source(item,value) + else: + self.add_map(n) + self.map[n].source=value + self.source.append(n) + + def set_target(self,n,value=True): + if isinstance(n, (list,tuple,set,frozenset)): + for item in n: + self.set_target(item,value) + else: + self.add_map(n) + self.map[n].target=value + self.target.append(n) - def add_blockage(self,block_list): - debug.info(2,"Adding blockage list={0}".format(str(block_list))) - for n in block_list: - self.set_blocked(n) + + def add_source(self,track_list,value=True): + debug.info(3,"Adding source list={0}".format(str(track_list))) + for n in track_list: + debug.info(4,"Adding source ={0}".format(str(n))) + self.set_source(n,value) + self.set_blocked(n,False) - def add_map(self,p): + + def add_target(self,track_list,value=True): + debug.info(3,"Adding target list={0}".format(str(track_list))) + for n in track_list: + debug.info(4,"Adding target ={0}".format(str(n))) + self.set_target(n,value) + self.set_blocked(n,False) + + def is_target(self,point): + """ + Point is in the target set, so we are done. + """ + return point in self.target + + def add_map(self,n): """ Add a point to the map if it doesn't exist. """ - if p not in self.map.keys(): - self.map[p]=cell() + if isinstance(n, (list,tuple,set,frozenset)): + for item in n: + self.add_map(item) + else: + if n not in self.map.keys(): + self.map[n]=grid_cell() - def add_path(self,path): + + def block_path(self,path): """ - Mark the path in the routing grid for visualization + Mark the path in the routing grid as blocked. + Also unsets the path flag. """ - self.path=path - for p in path: - self.map[p].path=True - - def cost(self,path): - """ - The cost of the path is the length plus a penalty for the number - of vias. We assume that non-preferred direction is penalized. - """ - - # Ignore the source pin layer change, FIXME? - def pairwise(iterable): - "s -> (s0,s1), (s1,s2), (s2, s3), ..." - a, b = tee(iterable) - next(b, None) - return zip(a, b) - - - plist = pairwise(path) - cost = 0 - for p0,p1 in plist: - if p0.z != p1.z: # via - cost += self.VIA_COST - elif p0.x != p1.x: # horizontal - cost += self.NONPREFERRED_COST if (p0.z == 1) else self.PREFERRED_COST - elif p0.y != p1.y: # vertical - cost += self.NONPREFERRED_COST if (p0.z == 0) else self.PREFERRED_COST - else: - debug.error("Non-changing direction!") - - return cost + path.set_path(False) + path.set_blocked(True) + diff --git a/compiler/router/cell.py b/compiler/router/grid_cell.py similarity index 93% rename from compiler/router/cell.py rename to compiler/router/grid_cell.py index e70e3474..cb78116c 100644 --- a/compiler/router/cell.py +++ b/compiler/router/grid_cell.py @@ -1,10 +1,9 @@ -class cell: +class grid_cell: """ A single cell that can be occupied in a given layer, blocked, visited, etc. """ def __init__(self): - self.visited = False self.path = False self.blocked = False self.source = False @@ -17,13 +16,17 @@ class cell: Reset the dynamic info about routing. The pins/blockages are not reset so that they can be reused. """ - self.visited=False self.min_cost=-1 self.min_path=None self.blocked=False self.source=False self.target=False + def get_cost(self): + # We can display the cost of the frontier + if self.min_cost > 0: + return self.min_cost + def get_type(self): if self.blocked: @@ -38,8 +41,4 @@ class cell: if self.path: return "P" - # We can display the cost of the frontier - if self.min_cost > 0: - return self.min_cost - return None diff --git a/compiler/router/grid_path.py b/compiler/router/grid_path.py new file mode 100644 index 00000000..cbe739ef --- /dev/null +++ b/compiler/router/grid_path.py @@ -0,0 +1,212 @@ +import debug +from vector3d import vector3d +from itertools import tee +from grid import grid +from grid_cell import grid_cell +from direction import direction + +class grid_path: + """ + A grid path is a list of lists of grid cells. + It can have a width that is more than one cell. + All of the sublists will be the same dimension. + Cells should be continguous. + It can have a name to define pin shapes as well. + """ + + def __init__(self, items=[], name=""): + self.name = name + if items: + self.pathlist = [items] + else: + self.pathlist = [] + + def __str__(self): + #import pprint + p = str(self.pathlist) #pprint.pformat(self.pathlist) + if self.name != "": + return (str(self.name) + " : " + p) + return p + + def __setitem__(self, index, value): + """ + override setitem function + can set value by pathinstance[index]=value + """ + self.pathlist[index]=value + + def __getitem__(self, index): + """ + override getitem function + can get value by value=pathinstance[index] + """ + return self.pathlist[index] + + def __contains__(self, key): + """ + Determine if cell exists in this path + """ + # FIXME: Could maintain a hash to make in O(1) + for sublist in self.pathlist: + for item in sublist: + if item == key: + return True + else: + return False + + def __add__(self, items): + """ + Override add to do append + """ + return self.pathlist.extend(items) + + def __len__(self): + return len(self.pathlist) + + def trim_last(self): + """ + Drop the last item + """ + if len(self.pathlist)>0: + self.pathlist.pop() + + def trim_first(self): + """ + Drop the first item + """ + if len(self.pathlist)>0: + self.pathlist.pop(0) + + def append(self,item): + """ + Append the list of items to the cells + """ + self.pathlist.append(item) + + def extend(self,item): + """ + Extend the list of items to the cells + """ + self.pathlist.extend(item) + + def set_path(self,value=True): + for sublist in self.pathlist: + for p in sublist: + p.path=value + + def set_blocked(self,value=True): + for sublist in self.pathlist: + for p in sublist: + p.blocked=value + + def get_grids(self): + """ + Return a set of all the grids in this path. + """ + newset = set() + for sublist in self.pathlist: + newset.update(sublist) + return newset + + def get_wire_grids(self, start_index, end_index): + """ + Return a set of all the wire grids in this path. + These are the indices in the wave path in a certain range. + """ + newset = set() + for sublist in self.pathlist: + newset.update(sublist[start_index:end_index]) + return newset + + def cost(self): + """ + The cost of the path is the length plus a penalty for the number + of vias. We assume that non-preferred direction is penalized. + This cost only works with 1 wide tracks. + """ + + # Ignore the source pin layer change, FIXME? + def pairwise(iterable): + "s -> (s0,s1), (s1,s2), (s2, s3), ..." + a, b = tee(iterable) + next(b, None) + return zip(a, b) + + plist = list(pairwise(self.pathlist)) + cost = 0 + for p0list,p1list in plist: + # This is because they are "waves" so pick the first item + p0=p0list[0] + p1=p1list[0] + + if p0.z != p1.z: # via + cost += grid.VIA_COST + elif p0.x != p1.x and p0.z==1: # horizontal on vertical layer + cost += grid.NONPREFERRED_COST + elif p0.y != p1.y and p0.z==0: # vertical on horizontal layer + cost += grid.NONPREFERRED_COST + else: + cost += grid.PREFERRED_COST + + return cost + + def expand_dirs(self): + """ + Expand from the end in each of the four cardinal directions plus up + or down but not expanding to blocked cells. Expands in all + directions regardless of preferred directions. + + If the width is more than one, it can only expand in one direction + (for now). This is assumed for the supply router for now. + + """ + neighbors = [] + + for d in direction.cardinal_directions(True): + n = self.neighbor(d) + if n: + neighbors.append(n) + + return neighbors + + def neighbor(self, d): + offset = direction.get_offset(d) + + newwave = [point + offset for point in self.pathlist[-1]] + + if newwave in self.pathlist: + return None + elif newwave[0].z>1 or newwave[0].z<0: + return None + + return newwave + + + def set_layer(self, zindex): + new_pathlist = [vector3d(item.x, item.y, zindex) for wave in self.pathlist for item in wave] + self.pathlist = new_pathlist + + + def overlap(self, other): + """ + Return the overlap waves ignoring different layers + """ + + my_zindex = self.pathlist[0][0].z + other_flat_cells = [vector3d(item.x,item.y,my_zindex) for wave in other.pathlist for item in wave] + # This keeps the wave structure of the self layer + shared_waves = [] + for wave in self.pathlist: + for item in wave: + # If any item in the wave is not contained, skip it + if not item in other_flat_cells: + break + else: + shared_waves.append(wave) + + if len(shared_waves)>0: + ll = shared_waves[0][0] + ur = shared_waves[-1][-1] + return [ll,ur] + return None + diff --git a/compiler/router/grid_utils.py b/compiler/router/grid_utils.py new file mode 100644 index 00000000..7ad864aa --- /dev/null +++ b/compiler/router/grid_utils.py @@ -0,0 +1,155 @@ +""" +Some utility functions for sets of grid cells. +""" + +import debug +import math +from direction import direction +from vector3d import vector3d + + +def increment_set(curset, direct): + """ + Return the cells incremented in given direction + """ + offset = direction.get_offset(direct) + + newset = set() + for c in curset: + newc = c+offset + newset.add(newc) + + return newset + + +def remove_border(curset, direct): + """ + Remove the cells on a given border. + """ + border = get_border(curset, direct) + curset.difference_update(border) + + +def get_upper_right(curset): + ur = None + for p in curset: + if ur == None or (p.x>=ur.x and p.y>=ur.y): + ur = p + return ur + +def get_lower_left(curset): + ll = None + for p in curset: + if ll == None or (p.x<=ll.x and p.y<=ll.y): + ll = p + return ll + +def get_border( curset, direct): + """ + Return the furthest cell(s) in a given direction. + """ + + # find direction-most cell(s) + maxc = [] + if direct==direction.NORTH: + for c in curset: + if len(maxc)==0 or c.y>maxc[0].y: + maxc = [c] + elif c.y==maxc[0].y: + maxc.append(c) + elif direct==direct.SOUTH: + for c in curset: + if len(maxc)==0 or c.ymaxc[0].x: + maxc = [c] + elif c.x==maxc[0].x: + maxc.append(c) + elif direct==direct.WEST: + for c in curset: + if len(maxc)==0 or c.x=pin.uy(): + above_item = item + else: + break + + # There was nothing + if above_item==None: + return None + # If it already overlaps, no connector needed + if above_item.overlaps(pin): + return None + + # Otherwise, make a connector to the item + p = self.compute_connector(pin, above_item) + return p + + def find_below_connector(self, pin, enclosures): + """ + Find the enclosure that is below the pin + and make a connector to it's upper edge. + """ + # Create the list of shapes that contain the pin edge + edge_list = [] + for shape in enclosures: + if shape.xcontains(pin): + edge_list.append(shape) + + # Sort them by their upper edge + edge_list.sort(key=lambda x: x.uy()) + + # Find the upper edge that is next to the pin's bottom edge + bottom_item = None + for item in edge_list: + if item.uy()<=pin.by(): + bottom_item = item + else: + break + + # There was nothing to the left + if bottom_item==None: + return None + # If it already overlaps, no connector needed + if bottom_item.overlaps(pin): + return None + + # Otherwise, make a connector to the item + p = self.compute_connector(pin, bottom_item) + return p + + def find_left_connector(self, pin, enclosures): + """ + Find the enclosure that is to the left of the pin + and make a connector to it's right edge. + """ + # Create the list of shapes that contain the pin edge + edge_list = [] + for shape in enclosures: + if shape.ycontains(pin): + edge_list.append(shape) + + # Sort them by their right edge + edge_list.sort(key=lambda x: x.rx()) + + # Find the right edge that is to the pin's left edge + left_item = None + for item in edge_list: + if item.rx()<=pin.lx(): + left_item = item + else: + break + + # There was nothing to the left + if left_item==None: + return None + # If it already overlaps, no connector needed + if left_item.overlaps(pin): + return None + + # Otherwise, make a connector to the item + p = self.compute_connector(pin, left_item) + return p + + def find_right_connector(self, pin, enclosures): + """ + Find the enclosure that is to the right of the pin + and make a connector to it's left edge. + """ + # Create the list of shapes that contain the pin edge + edge_list = [] + for shape in enclosures: + if shape.ycontains(pin): + edge_list.append(shape) + + # Sort them by their right edge + edge_list.sort(key=lambda x: x.lx(), reverse=True) + + # Find the left edge that is next to the pin's right edge + right_item = None + for item in edge_list: + if item.lx()>=pin.rx(): + right_item = item + else: + break + + # There was nothing to the right + if right_item==None: + return None + # If it already overlaps, no connector needed + if right_item.overlaps(pin): + return None + + # Otherwise, make a connector to the item + p = self.compute_connector(pin, right_item) + return p + + def find_smallest_connector(self, pin_list, shape_list): + """ + Compute all of the connectors between the overlapping pins and enclosure shape list.. + Return the smallest. + """ + smallest = None + for pin in pin_list: + for enclosure in shape_list: + new_enclosure = self.compute_connector(pin, enclosure) + if smallest == None or new_enclosure.area() min_width: + if smallest_shape == None or other.area() biggest.area(): + biggest = pin + + return pin + + def enclose_pin_grids(self, ll, dir1=direction.NORTH, dir2=direction.EAST): + """ + This encloses a single pin component with a rectangle + starting with the seed and expanding right until blocked + and then up until blocked. + dir1 and dir2 should be two orthogonal directions. + """ + + offset1= direction.get_offset(dir1) + offset2= direction.get_offset(dir2) + + # We may have started with an empty set + if not self.grids: + return None + + # Start with the ll and make the widest row + row = [ll] + # Move in dir1 while we can + while True: + next_cell = row[-1] + offset1 + # Can't move if not in the pin shape + if next_cell in self.grids and next_cell not in self.router.blocked_grids: + row.append(next_cell) + else: + break + # Move in dir2 while we can + while True: + next_row = [x+offset2 for x in row] + for cell in next_row: + # Can't move if any cell is not in the pin shape + if cell not in self.grids or cell in self.router.blocked_grids: + break + else: + row = next_row + # Skips the second break + continue + # Breaks from the nested break + break + + # Add a shape from ll to ur + ur = row[-1] + return (ll,ur) + + + def enclose_pin(self): + """ + If there is one set of connected pin shapes, + this will find the smallest rectangle enclosure that overlaps with any pin. + If there is not, it simply returns all the enclosures. + """ + self.enclosed = True + + # Compute the enclosure pin_layout list of the set of tracks + self.enclosures = self.compute_enclosures() + + for pin_list in self.pins: + for pin in pin_list: + + # If it is contained, it won't need a connector + if pin.contained_by_any(self.enclosures): + continue + + # Find a connector in the cardinal directions + # If there is overlap, but it isn't contained, these could all be None + # These could also be none if the pin is diagonal from the enclosure + left_connector = self.find_left_connector(pin, self.enclosures) + right_connector = self.find_right_connector(pin, self.enclosures) + above_connector = self.find_above_connector(pin, self.enclosures) + below_connector = self.find_below_connector(pin, self.enclosures) + connector_list = [left_connector, right_connector, above_connector, below_connector] + filtered_list = list(filter(lambda x: x!=None, connector_list)) + if (len(filtered_list)>0): + import copy + bbox_connector = copy.copy(pin) + bbox_connector.bbox(filtered_list) + self.enclosures.append(bbox_connector) + + # Now, make sure each pin touches an enclosure. If not, add another (diagonal) connector. + # This could only happen when there was no enclosure in any cardinal direction from a pin + for pin_list in self.pins: + if not self.overlap_any_shape(pin_list, self.enclosures): + connector = self.find_smallest_connector(pin_list, self.enclosures) + if connector==None: + debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures)) + self.router.write_debug_gds("no_connector.gds") + self.enclosures.append(connector) + + + debug.info(3,"Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name, + self.pins, + self.grids, + self.enclosures)) + + def combine_groups(self, pg1, pg2): + """ + Combine two pin groups into one. + """ + self.pins = [*pg1.pins, *pg2.pins] # Join the two lists of pins + self.grids = pg1.grids | pg2.grids # OR the set of grid locations + self.secondary_grids = pg1.secondary_grids | pg2.secondary_grids + + def add_group(self, pg): + """ + Combine the pin group into this one. This will add to the first item in the pins + so this should be used before there are disconnected pins. + """ + debug.check(len(self.pins)==1,"Don't know which group to add pins to.") + self.pins[0].update(*pg.pins) # Join the two lists of pins + self.grids |= pg.grids # OR the set of grid locations + self.secondary_grids |= pg.secondary_grids + + def add_enclosure(self, cell): + """ + Add the enclosure shape to the given cell. + """ + for enclosure in self.enclosures: + debug.info(2,"Adding enclosure {0} {1}".format(self.name, enclosure)) + cell.add_rect(layer=enclosure.layer, + offset=enclosure.ll(), + width=enclosure.width(), + height=enclosure.height()) + + + def perimeter_grids(self): + """ + Return a list of the grids on the perimeter. + This assumes that we have a single contiguous shape. + """ + perimeter_set = set() + cardinal_offsets = direction.cardinal_offsets() + for g1 in self.grids: + neighbor_grids = [g1 + offset for offset in cardinal_offsets] + neighbor_count = sum([x in self.grids for x in neighbor_grids]) + # If we aren't completely enclosed, we are on the perimeter + if neighbor_count < 4: + perimeter_set.add(g1) + + return perimeter_set + + def adjacent(self, other): + """ + Chck if the two pin groups have at least one adjacent pin grid. + """ + # We could optimize this to just check the boundaries + for g1 in self.perimeter_grids(): + for g2 in other.perimeter_grids(): + if g1.adjacent(g2): + return True + + return False + + + def adjacent_grids(self, other, separation): + """ + Determine the sets of grids that are within a separation distance + of any grid in the other set. + """ + # We could optimize this to just check the boundaries + adj_grids = set() + for g1 in self.grids: + for g2 in other.grids: + if g1.distance(g2) <= separation: + adj_grids.add(g1) + + return adj_grids + + def convert_pin(self): + """ + Convert the list of pin shapes into sets of routing grids. + The secondary set of grids are "optional" pin shapes that could be + should be either blocked or part of the pin. + """ + pin_set = set() + partial_set = set() + blockage_set = set() + + for pin_list in self.pins: + for pin in pin_list: + debug.info(2," Converting {0}".format(pin)) + # Determine which tracks the pin overlaps + (sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin) + pin_set.update(sufficient) + partial_set.update(insufficient) + + # Blockages will be a super-set of pins since it uses the inflated pin shape. + blockage_in_tracks = self.router.convert_blockage(pin) + blockage_set.update(blockage_in_tracks) + + # If we have a blockage, we must remove the grids + # Remember, this excludes the pin blockages already + shared_set = pin_set & self.router.blocked_grids + if len(shared_set)>0: + debug.info(2,"Removing pins {}".format(shared_set)) + pin_set.difference_update(shared_set) + shared_set = partial_set & self.router.blocked_grids + if len(shared_set)>0: + debug.info(2,"Removing pins {}".format(shared_set)) + partial_set.difference_update(shared_set) + shared_set = blockage_set & self.router.blocked_grids + if len(shared_set)>0: + debug.info(2,"Removing blocks {}".format(shared_set)) + blockage_set.difference_update(shared_set) + + # At least one of the groups must have some valid tracks + if (len(pin_set)==0 and len(partial_set)==0 and len(blockage_set)==0): + #debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins)) + + for pin_list in self.pins: + for pin in pin_list: + debug.warning(" Expanding conversion {0}".format(pin)) + # Determine which tracks the pin overlaps + (sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin, expansion=1) + pin_set.update(sufficient) + partial_set.update(insufficient) + + if len(pin_set)==0 and len(partial_set)==0: + debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins)) + self.router.write_debug_gds("blocked_pin.gds") + + # Consider all the grids that would be blocked + self.grids = pin_set | partial_set + # Remember the secondary grids for removing adjacent pins + self.secondary_grids = partial_set + + debug.info(2," pins {}".format(self.grids)) + debug.info(2," secondary {}".format(self.secondary_grids)) + + # def recurse_simple_overlap_enclosure(self, start_set, direct): + # """ + # Recursive function to return set of tracks that connects to + # the actual supply rail wire in a given direction (or terminating + # when any track is no longer in the supply rail. + # """ + # next_set = grid_utils.expand_border(start_set, direct) + + # supply_tracks = self.router.supply_rail_tracks[self.name] + # supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name] + + # supply_overlap = next_set & supply_tracks + # wire_overlap = next_set & supply_wire_tracks + + # # If the rail overlap is the same, we are done, since we connected to the actual wire + # if len(wire_overlap)==len(start_set): + # new_set = start_set | wire_overlap + # # If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region + # elif len(supply_overlap)==len(start_set): + # recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct) + # new_set = start_set | supply_overlap | recurse_set + # else: + # # If we got no next set, we are done, can't expand! + # new_set = set() + + # return new_set + + # def create_simple_overlap_enclosure(self, start_set): + # """ + # This takes a set of tracks that overlap a supply rail and creates an enclosure + # that is ensured to overlap the supply rail wire. + # It then adds rectangle(s) for the enclosure. + # """ + # additional_set = set() + # # Check the layer of any element in the pin to determine which direction to route it + # e = next(iter(start_set)) + # new_set = start_set.copy() + # if e.z==0: + # new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH) + # if not new_set: + # new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH) + # else: + # new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST) + # if not new_set: + # new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST) + + # # Expand the pin grid set to include some extra grids that connect the supply rail + # self.grids.update(new_set) + + # # Block the grids + # self.blockages.update(new_set) + + # # Add the polygon enclosures and set this pin group as routed + # self.set_routed() + # self.enclosures = self.compute_enclosures() + + + + diff --git a/compiler/router/router.py b/compiler/router/router.py index 73c0681d..35650994 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -1,113 +1,127 @@ +import sys import gdsMill -import tech -from contact import contact +from tech import drc,GDS +from tech import layer as techlayer import math import debug +from router_tech import router_tech from pin_layout import pin_layout +from pin_group import pin_group from vector import vector from vector3d import vector3d -from globals import OPTS +from globals import OPTS,print_time +from pprint import pformat +import grid_utils +from datetime import datetime -class router: +class router(router_tech): """ A router class to read an obstruction map from a gds and plan a route on a given layer. This is limited to two layer routes. It populates blockages on a grid class. """ - def __init__(self, gds_name=None, module=None): - """Use the gds file or the cell for the blockages with the top module topName and - layers for the layers to route on + def __init__(self, layers, design, gds_filename=None, rail_track_width=1): """ - self.gds_name = gds_name - self.module = module - debug.check(not (gds_name and module), "Specify only a GDS file or module") - - # If we specified a module instead, write it out to read the gds - # This isn't efficient, but easy for now - if module: - gds_name = OPTS.openram_temp+"temp.gds" - module.gds_write(gds_name) + This will instantiate a copy of the gds file or the module at (0,0) and + route on top of this. The blockages from the gds/module will be considered. + """ + router_tech.__init__(self, layers, rail_track_width) - # Load the gds file and read in all the shapes - self.layout = gdsMill.VlsiLayout(units=tech.GDS["unit"]) - self.reader = gdsMill.Gds2reader(self.layout) - self.reader.loadFromFile(gds_name) - self.top_name = self.layout.rootStructureName + self.cell = design + # If didn't specify a gds blockage file, write it out to read the gds + # This isn't efficient, but easy for now + #start_time = datetime.now() + if not gds_filename: + gds_filename = OPTS.openram_temp+"temp.gds" + self.cell.gds_write(gds_filename) + + # Load the gds file and read in all the shapes + self.layout = gdsMill.VlsiLayout(units=GDS["unit"]) + self.reader = gdsMill.Gds2reader(self.layout) + self.reader.loadFromFile(gds_filename) + self.top_name = self.layout.rootStructureName + #print_time("GDS read",datetime.now(), start_time) + + ### The pin data structures + # A map of pin names to a set of pin_layout structures + # (i.e. pins with a given label) self.pins = {} + # This is a set of all pins (ignoring names) so that can quickly not create blockages for pins + # (They will be blocked when we are routing other nets based on their name.) + self.all_pins = set() + + # The labeled pins above categorized into pin groups that are touching/connected. + self.pin_groups = {} + + ### The blockage data structures + # A list of metal shapes (using the same pin_layout structure) that are not pins but blockages. self.blockages=[] - # all the paths we've routed so far (to supplement the blockages) + # The corresponding set of blocked grids for above pin shapes + self.blocked_grids = set() + + ### The routed data structures + # A list of paths that have been "routed" self.paths = [] + # A list of path blockages (they might be expanded for wide metal DRC) + self.path_blockages = [] # The boundary will determine the limits to the size of the routing grid self.boundary = self.layout.measureBoundary(self.top_name) - self.ll = vector(self.boundary[0]) - self.ur = vector(self.boundary[1]) + # These must be un-indexed to get rid of the matrix type + self.ll = vector(self.boundary[0][0], self.boundary[0][1]) + self.ur = vector(self.boundary[1][0], self.boundary[1][1]) + def clear_pins(self): + """ + Convert the routed path to blockages. + Keep the other blockages unchanged. + """ + self.pins = {} + self.all_pins = set() + self.pin_groups = {} + # DO NOT clear the blockages as these don't change + self.rg.reinit() + + def set_top(self,top_name): """ If we want to route something besides the top-level cell.""" self.top_name = top_name - def get_zindex(self,layer_num): - if layer_num==self.horiz_layer_number: - return 0 - else: - return 1 - - def set_layers(self, layers): - """Allows us to change the layers that we are routing on. First layer - is always horizontal, middle is via, and last is always - vertical. - """ - self.layers = layers - (horiz_layer, via_layer, vert_layer) = self.layers - - self.vert_layer_name = vert_layer - self.vert_layer_width = tech.drc["minwidth_{0}".format(vert_layer)] - self.vert_layer_spacing = tech.drc[str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name)] - self.vert_layer_number = tech.layer[vert_layer] - self.horiz_layer_name = horiz_layer - self.horiz_layer_width = tech.drc["minwidth_{0}".format(horiz_layer)] - self.horiz_layer_spacing = tech.drc[str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name)] - self.horiz_layer_number = tech.layer[horiz_layer] - - # Contacted track spacing. - via_connect = contact(self.layers, (1, 1)) - self.max_via_size = max(via_connect.width,via_connect.height) - self.horiz_track_width = self.max_via_size + self.horiz_layer_spacing - self.vert_track_width = self.max_via_size + self.vert_layer_spacing - - # We'll keep horizontal and vertical tracks the same for simplicity. - self.track_width = max(self.horiz_track_width,self.vert_track_width) - debug.info(1,"Track width: "+str(self.track_width)) - - self.track_widths = [self.track_width] * 2 - self.track_factor = [1/self.track_width] * 2 - debug.info(1,"Track factor: {0}".format(self.track_factor)) - - - - - def find_pin(self,pin_name): - """ - Finds the pin shapes and converts to tracks. - Pin can either be a label or a location,layer pair: [[x,y],layer]. + def is_wave(self,path): """ + Determines if this is a multi-track width wave (True) or a normal route (False) + """ + return len(path[0])>1 + - shape_list=self.layout.getPinShapeByLabel(str(pin_name)) - pin_list = [] + def retrieve_pins(self,pin_name): + """ + Retrieve the pin shapes on metal 3 from the layout. + """ + debug.info(2,"Retrieving pins for {}.".format(pin_name)) + shape_list=self.layout.getAllPinShapes(str(pin_name)) + pin_set = set() for shape in shape_list: - (name,layer,boundary)=shape - rect = [vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])] + (layer,boundary)=shape + # GDSMill boundaries are in (left, bottom, right, top) order + # so repack and snap to the grid + ll = vector(boundary[0],boundary[1]).snap_to_grid() + ur = vector(boundary[2],boundary[3]).snap_to_grid() + rect = [ll,ur] pin = pin_layout(pin_name, rect, layer) - pin_list.append(pin) + pin_set.add(pin) - debug.check(len(pin_list)>0,"Did not find any pin shapes for {0}.".format(str(pin))) + debug.check(len(pin_set)>0,"Did not find any pin shapes for {0}.".format(str(pin_name))) - return pin_list + self.pins[pin_name] = pin_set + self.all_pins.update(pin_set) + for pin in self.pins[pin_name]: + debug.info(3,"Retrieved pin {}".format(str(pin))) + def find_blockages(self): """ @@ -115,39 +129,240 @@ class router: This doesn't consider whether the obstacles will be pins or not. They get reset later if they are not actually a blockage. """ - #for layer in [self.vert_layer_number,self.horiz_layer_number]: - # self.get_blockages(layer) - self.get_blockages(self.horiz_layer_number) + debug.info(1,"Finding blockages.") + for layer in [self.vert_layer_number,self.horiz_layer_number]: + self.retrieve_blockages(layer) - def clear_pins(self): + def find_pins_and_blockages(self, pin_list): """ - Reset the source and destination pins to start a new routing. - Convert the source/dest pins to blockages. - Convert the routed path to blockages. - Keep the other blockages unchanged. + Find the pins and blockages in the design """ - self.pins = {} - # DO NOT clear the blockages as these don't change - self.rg.reinit() + # This finds the pin shapes and sorts them into "groups" that are connected + # This must come before the blockages, so we can not count the pins themselves + # as blockages. + start_time = datetime.now() + for pin_name in pin_list: + self.retrieve_pins(pin_name) + print_time("Retrieving pins",datetime.now(), start_time, 4) + start_time = datetime.now() + for pin_name in pin_list: + self.analyze_pins(pin_name) + print_time("Analyzing pins",datetime.now(), start_time, 4) + + # This will get all shapes as blockages and convert to grid units + # This ignores shapes that were pins + start_time = datetime.now() + self.find_blockages() + print_time("Finding blockages",datetime.now(), start_time, 4) + + # Convert the blockages to grid units + start_time = datetime.now() + self.convert_blockages() + print_time("Converting blockages",datetime.now(), start_time, 4) + + # This will convert the pins to grid units + # It must be done after blockages to ensure no DRCs between expanded pins and blocked grids + start_time = datetime.now() + for pin in pin_list: + self.convert_pins(pin) + print_time("Converting pins",datetime.now(), start_time, 4) + + # Combine adjacent pins into pin groups to reduce run-time + # by reducing the number of maze routes. + # This algorithm is > O(n^2) so remove it for now + # start_time = datetime.now() + # for pin in pin_list: + # self.combine_adjacent_pins(pin) + # print_time("Combining adjacent pins",datetime.now(), start_time, 4) + + + # Separate any adjacent grids of differing net names that overlap + # Must be done before enclosing pins + start_time = datetime.now() + self.separate_adjacent_pins(0) + print_time("Separating adjacent pins",datetime.now(), start_time, 4) + + # Enclose the continguous grid units in a metal rectangle to fix some DRCs + start_time = datetime.now() + self.enclose_pins() + print_time("Enclosing pins",datetime.now(), start_time, 4) + + def combine_adjacent_pins(self, pin_name): + """ + Find pins that have adjacent routing tracks and merge them into a + single pin_group. The pins themselves may not be touching, but + enclose_pins in the next step will ensure they are touching. + """ + debug.info(1,"Combining adjacent pins for {}.".format(pin_name)) + # Find all adjacencies + adjacent_pins = {} + for index1,pg1 in enumerate(self.pin_groups[pin_name]): + for index2,pg2 in enumerate(self.pin_groups[pin_name]): + # Cannot combine with yourself, also don't repeat + if index1<=index2: + continue + # Combine if at least 1 grid cell is adjacent + if pg1.adjacent(pg2): + if not index1 in adjacent_pins.keys(): + adjacent_pins[index1] = set([index2]) + else: + adjacent_pins[index1].add(index2) + + # Make a list of indices to ensure every group gets in the new set + all_indices = set([x for x in range(len(self.pin_groups[pin_name]))]) + + # Now reconstruct the new groups + new_pin_groups = [] + for index1,index2_set in adjacent_pins.items(): + # Remove the indices if they are added to the new set + all_indices.discard(index1) + all_indices.difference_update(index2_set) + + # Create the combined group starting with the first item + combined = self.pin_groups[pin_name][index1] + # Add all of the other items that overlapped + for index2 in index2_set: + pg = self.pin_groups[pin_name][index2] + combined.add_group(pg) + debug.info(3,"Combining {0} {1}:".format(pin_name, index2)) + debug.info(3, " {0}\n {1}".format(combined.pins, pg.pins)) + debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids)) + new_pin_groups.append(combined) + + # Add the pin groups that weren't added to the new set + for index in all_indices: + new_pin_groups.append(self.pin_groups[pin_name][index]) + + old_size = len(self.pin_groups[pin_name]) + # Use the new pin group! + self.pin_groups[pin_name] = new_pin_groups + removed_pairs = old_size - len(new_pin_groups) + debug.info(1, "Combined {0} pin groups for {1}".format(removed_pairs,pin_name)) + + return removed_pairs + + + def separate_adjacent_pins(self, separation): + """ + This will try to separate all grid pins by the supplied number of separation + tracks (default is to prevent adjacency). + """ + # Commented out to debug with SCMOS + #if separation==0: + # return + + pin_names = self.pin_groups.keys() + for i,pin_name1 in enumerate(pin_names): + for j,pin_name2 in enumerate(pin_names): + if i==j: + continue + if i>j: + return + self.separate_adjacent_pin(pin_name1, pin_name2, separation) + + def separate_adjacent_pin(self, pin_name1, pin_name2, separation): + """ + Go through all of the pin groups and check if any other pin group is + within a separation of it. + If so, reduce the pin group grid to not include the adjacent grid. + Try to do this intelligently to keep th pins enclosed. + """ + debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2)) + removed_grids = 0 + for index1,pg1 in enumerate(self.pin_groups[pin_name1]): + for index2,pg2 in enumerate(self.pin_groups[pin_name2]): + adj_grids = pg1.adjacent_grids(pg2, separation) + removed_grids += len(adj_grids) + # These should have the same length, so... + if len(adj_grids)>0: + debug.info(3,"Adjacent grids {0} {1} adj={2}".format(index1,index2,adj_grids)) + self.remove_adjacent_grid(pg1, pg2, adj_grids) + + + debug.info(1,"Removed {} adjacent grids.".format(removed_grids)) + + def remove_adjacent_grid(self, pg1, pg2, adj_grids): + """ + Remove one of the adjacent grids in a heuristic manner. + This will try to keep the groups similar sized by removing from the bigger group. + """ + + if pg1.size()>pg2.size(): + bigger = pg1 + smaller = pg2 + else: + bigger = pg2 + smaller = pg1 + + for adj in adj_grids: + + + # If the adjacent grids are a subset of the secondary grids (i.e. not necessary) + # remove them from each + if adj in bigger.secondary_grids: + debug.info(3,"Removing {} from bigger secondary {}".format(adj, bigger)) + bigger.grids.remove(adj) + bigger.secondary_grids.remove(adj) + self.blocked_grids.add(adj) + elif adj in smaller.secondary_grids: + debug.info(3,"Removing {} from smaller secondary {}".format(adj, smaller)) + smaller.grids.remove(adj) + smaller.secondary_grids.remove(adj) + self.blocked_grids.add(adj) + else: + # If we couldn't remove from a secondary grid, we must remove from the primary + # grid of at least one pin + if adj in bigger.grids: + debug.info(3,"Removing {} from bigger primary {}".format(adj, bigger)) + bigger.grids.remove(adj) + elif adj in smaller.grids: + debug.info(3,"Removing {} from smaller primary {}".format(adj, smaller)) + smaller.grids.remove(adj) + + - def translate_coordinates(self, coord, mirr, angle, xyShift): + def prepare_blockages(self, pin_name): """ - Calculate coordinates after flip, rotate, and shift + Reset and add all of the blockages in the design. + Names is a list of pins to add as a blockage. """ - coordinate = [] - for item in coord: - x = (item[0]*math.cos(angle)-item[1]*mirr*math.sin(angle)+xyShift[0]) - y = (item[0]*math.sin(angle)+item[1]*mirr*math.cos(angle)+xyShift[1]) - coordinate += [(x, y)] - return coordinate + debug.info(3,"Preparing blockages.") + + # Start fresh. Not the best for run-time, but simpler. + self.clear_blockages() + # This adds the initial blockges of the design + #print("BLOCKING:",self.blocked_grids) + self.set_blockages(self.blocked_grids,True) + # Block all of the supply rails (some will be unblocked if they're a target) + self.set_supply_rail_blocked(True) + + # Block all of the pin components (some will be unblocked if they're a source/target) + # Also block the previous routes + for name in self.pin_groups.keys(): + blockage_grids = {y for x in self.pin_groups[name] for y in x.grids} + self.set_blockages(blockage_grids,True) + blockage_grids = {y for x in self.pin_groups[name] for y in x.blockages} + self.set_blockages(blockage_grids,True) + + # FIXME: These duplicate a bit of work + # These are the paths that have already been routed. + self.set_blockages(self.path_blockages) + + # Don't mark the other components as targets since we want to route + # directly to a rail, but unblock all the source components so we can + # route over them + blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids} + self.set_blockages(blockage_grids,False) + + def convert_shape_to_units(self, shape): """ Scale a shape (two vector list) to user units """ - unit_factor = [tech.GDS["unit"][0]] * 2 + unit_factor = [GDS["unit"][0]] * 2 ll=shape[0].scale(unit_factor) ur=shape[1].scale(unit_factor) return [ll,ur] @@ -179,90 +394,85 @@ class router: # z direction return 2 - - def contract_path(self,path): + def clear_blockages(self): """ - Remove intermediate points in a rectilinear path. + Clear all blockages on the grid. """ - newpath = [path[0]] - for i in range(1,len(path)-1): - prev_inertia=self.get_inertia(path[i-1],path[i]) - next_inertia=self.get_inertia(path[i],path[i+1]) - # if we switch directions, add the point, otherwise don't - if prev_inertia!=next_inertia: - newpath.append(path[i]) - - # always add the last path - newpath.append(path[-1]) - return newpath - - - def add_path_blockages(self): - """ - Go through all of the past paths and add them as blockages. - This is so we don't have to write/reload the GDS. - """ - for path in self.paths: - for grid in path: - self.rg.set_blocked(grid) + debug.info(3,"Clearing all blockages") + self.rg.clear_blockages() + def set_blockages(self, blockages, value=True): + """ Flag the blockages in the grid """ + self.rg.set_blocked(blockages, value) - def add_blockages(self): - """ Add the blockages except the pin shapes. Also remove the pin shapes from the blockages list. """ - # Join all the pin shapes into one big list - all_pins = [item for sublist in list(self.pins.values()) for item in sublist] + def get_blockage_tracks(self, ll, ur, z): + debug.info(3,"Converting blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) - # Do an n^2 check to see if any shapes are the same, otherwise add them - # FIXME: Make faster, but number of pins won't be *that* large - real_blockages = [] + block_list = [] + for x in range(int(ll[0]),int(ur[0])+1): + for y in range(int(ll[1]),int(ur[1])+1): + block_list.append(vector3d(x,y,z)) + + return set(block_list) + + def convert_blockage(self, blockage): + """ + Convert a pin layout blockage shape to routing grid tracks. + """ + # Inflate the blockage by half a spacing rule + [ll,ur]=self.convert_blockage_to_tracks(blockage.inflate()) + zlayer = self.get_zindex(blockage.layer_num) + blockage_tracks = self.get_blockage_tracks(ll, ur, zlayer) + return blockage_tracks + + def convert_blockages(self): + """ Convert blockages to grid tracks. """ + debug.info(1,"Converting blockages.") for blockage in self.blockages: - for pin in all_pins: - # If the blockage overlaps the pin and is on the same layer, - # it must be connected, so skip it. - if blockage==pin: - debug.info(1,"Removing blockage for pin {}".format(str(pin))) - break - else: - debug.info(2,"Adding blockage {}".format(str(blockage))) - # Inflate the blockage by spacing rule - [ll,ur]=self.convert_blockage_to_tracks(blockage.inflate()) - zlayer = self.get_zindex(blockage.layer_num) - self.rg.add_blockage_shape(ll,ur,zlayer) - real_blockages.append(blockage) - - # Remember the filtered blockages - self.blockages = real_blockages - + debug.info(3,"Converting blockage {}".format(str(blockage))) + blockage_list = self.convert_blockage(blockage) + self.blocked_grids.update(blockage_list) - def get_blockages(self, layer_num): + + def retrieve_blockages(self, layer_num): """ Recursive find boundaries as blockages to the routing grid. """ - shapes = self.layout.getAllShapesInStructureList(layer_num) + shapes = self.layout.getAllShapes(layer_num) for boundary in shapes: ll = vector(boundary[0],boundary[1]) ur = vector(boundary[2],boundary[3]) rect = [ll,ur] new_pin = pin_layout("blockage{}".format(len(self.blockages)),rect,layer_num) - self.blockages.append(new_pin) + + # If there is a rectangle that is the same in the pins, it isn't a blockage! + if new_pin not in self.all_pins: + self.blockages.append(new_pin) - def convert_point_to_units(self,p): + def convert_point_to_units(self, p): """ Convert a path set of tracks to center line path. """ pt = vector3d(p) - pt=pt.scale(self.track_widths[0],self.track_widths[1],1) + pt = pt.scale(self.track_widths[0],self.track_widths[1],1) return pt - def convert_blockage_to_tracks(self,shape): + def convert_wave_to_units(self, wave): + """ + Convert a wave to a set of center points + """ + return [self.convert_point_to_units(i) for i in wave] + + + def convert_blockage_to_tracks(self, shape): """ Convert a rectangular blockage shape into track units. """ (ll,ur) = shape - # ll = snap_to_grid(ll) - # ur = snap_to_grid(ur) + ll = snap_to_grid(ll) + ur = snap_to_grid(ur) # to scale coordinates to tracks debug.info(3,"Converting [ {0} , {1} ]".format(ll,ur)) @@ -274,160 +484,597 @@ class router: # and the track points are at the center ll = ll.round() ur = ur.round() - # if ll[0]<45 and ll[0]>35 and ll[1]<5 and ll[1]>-5: - # debug.info(0,"Converting [ {0} , {1} ]".format(old_ll,old_ur)) - # debug.info(0,"Converted [ {0} , {1} ]".format(ll,ur)) - # pin=self.convert_track_to_shape(ll) - # debug.info(0,"Pin {}".format(pin)) return [ll,ur] - def convert_pin_to_tracks(self, pin): + def convert_pin_to_tracks(self, pin_name, pin, expansion=0): """ Convert a rectangular pin shape into a list of track locations,layers. - If no on-grid pins are found, it searches for the nearest off-grid pin(s). - If a pin has insufficent overlap, it returns the blockage list to avoid it. + If no pins are "on-grid" (i.e. sufficient overlap) it makes the one with most overlap if it is not blocked. + If expansion>0, expamine areas beyond the current pin when it is blocked. """ (ll,ur) = pin.rect - #debug.info(1,"Converting [ {0} , {1} ]".format(ll,ur)) + debug.info(3,"Converting pin [ {0} , {1} ]".format(ll,ur)) # scale the size bigger to include neaby tracks ll=ll.scale(self.track_factor).floor() ur=ur.scale(self.track_factor).ceil() - # width depends on which layer it is + # Keep tabs on tracks with sufficient and insufficient overlap + sufficient_list = set() + insufficient_list = set() + zindex=self.get_zindex(pin.layer_num) - if zindex: - width = self.vert_layer_width - else: - width = self.horiz_layer_width + for x in range(int(ll[0])+expansion,int(ur[0])+1+expansion): + for y in range(int(ll[1]+expansion),int(ur[1])+1+expansion): + (full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex)) + if full_overlap: + sufficient_list.update([full_overlap]) + if partial_overlap: + insufficient_list.update([partial_overlap]) + debug.info(2,"Converting [ {0} , {1} ] full={2}".format(x,y, full_overlap)) - track_list = [] - block_list = [] + # Return all grids with any potential overlap (sufficient or not) + return (sufficient_list,insufficient_list) + - track_area = self.track_width*self.track_width - for x in range(ll[0],ur[0]): - for y in range(ll[1],ur[1]): - #debug.info(1,"Converting [ {0} , {1} ]".format(x,y)) - - # however, if there is not enough overlap, then if there is any overlap at all, - # we need to block it to prevent routes coming in on that grid - full_rect = self.convert_track_to_shape(vector3d(x,y,zindex)) - overlap_rect=self.compute_overlap(pin.rect,full_rect) - overlap_area = overlap_rect[0]*overlap_rect[1] - #debug.info(1,"Check overlap: {0} {1} max={2}".format(shape,rect,max_overlap)) - - # Assume if more than half the area, it is occupied - overlap_ratio = overlap_area/track_area - if overlap_ratio > 0.5: - track_list.append(vector3d(x,y,zindex)) - # otherwise, the pin may not be accessible, so block it - elif overlap_ratio > 0: - block_list.append(vector3d(x,y,zindex)) - else: - debug.info(4,"No overlap: {0} {1} max={2}".format(pin.rect,rect,max_overlap)) - print("H:",x,y) - if x>38 and x<42 and y>42 and y<45: - print(pin) - print(full_rect, overlap_rect, overlap_ratio) - #debug.warning("Off-grid pin for {0}.".format(str(pin))) - #debug.info(1,"Converted [ {0} , {1} ]".format(ll,ur)) - return (track_list,block_list) - - def compute_overlap(self,r1,r2): - """ Calculate the rectangular overlap of two rectangles. """ - (r1_ll,r1_ur) = r1 - (r2_ll,r2_ur) = r2 - - #ov_ur = vector(min(r1_ur.x,r2_ur.x),min(r1_ur.y,r2_ur.y)) - #ov_ll = vector(max(r1_ll.x,r2_ll.x),max(r1_ll.y,r2_ll.y)) - - dy = min(r1_ur.y,r2_ur.y)-max(r1_ll.y,r2_ll.y) - dx = min(r1_ur.x,r2_ur.x)-max(r1_ll.x,r2_ll.x) - - if dx>0 and dy>0: - return [dx,dy] - else: - return [0,0] + def get_all_offgrid_pin(self, pin, insufficient_list): + """ + Find a list of all pins with some overlap. + """ + #print("INSUFFICIENT LIST",insufficient_list) + # Find the coordinate with the most overlap + any_overlap = set() + for coord in insufficient_list: + full_pin = self.convert_track_to_pin(coord) + # Compute the overlap with that rectangle + overlap_rect=pin.compute_overlap(full_pin) + # Determine the max x or y overlap + max_overlap = max(overlap_rect) + if max_overlap>0: + any_overlap.update([coord]) + return any_overlap - def convert_track_to_pin(self,track): + def get_best_offgrid_pin(self, pin, insufficient_list): + """ + Find a list of the single pin with the most overlap. + """ + # Find the coordinate with the most overlap + best_coord = None + best_overlap = -math.inf + for coord in insufficient_list: + full_pin = self.convert_track_to_pin(coord) + # Compute the overlap with that rectangle + overlap_rect=pin.compute_overlap(full_pin) + # Determine the min x or y overlap + min_overlap = min(overlap_rect) + if min_overlap>best_overlap: + best_overlap=min_overlap + best_coord=coord + + return set([best_coord]) + + def get_furthest_offgrid_pin(self, pin, insufficient_list): + """ + Get a grid cell that is the furthest from the blocked grids. + """ + + # Find the coordinate with the most overlap + best_coord = None + best_dist = math.inf + for coord in insufficient_list: + min_dist = grid_utils.distance_set(coord, self.blocked_grids) + if min_dist 0: + debug.info(2," Overlap: {0} >? {1}".format(overlap_length,0)) + return (coord,None) + # If it overlaps with the inflated pin, it is partial + elif inflated_overlap_length==math.inf or inflated_overlap_length > 0: + debug.info(2," Partial overlap: {0} >? {1}".format(inflated_overlap_length,0)) + return (None,coord) + else: + debug.info(2," No overlap: {0} {1}".format(overlap_length,0)) + return (None,None) + + + def convert_track_to_pin(self, track): """ Convert a grid point into a rectangle shape that is centered track in the track and leaves half a DRC space in each direction. """ - # space depends on which layer it is - if track[2]==0: - space = 0.5*self.horiz_layer_spacing - else: - space = 0.5*self.vert_layer_spacing - # calculate lower left - x = track.x*self.track_width - 0.5*self.track_width + space - y = track.y*self.track_width - 0.5*self.track_width + space + x = track.x*self.track_width - 0.5*self.track_width + 0.5*self.track_space + y = track.y*self.track_width - 0.5*self.track_width + 0.5*self.track_space ll = snap_to_grid(vector(x,y)) # calculate upper right - x = track.x*self.track_width + 0.5*self.track_width - space - y = track.y*self.track_width + 0.5*self.track_width - space + x = track.x*self.track_width + 0.5*self.track_width - 0.5*self.track_space + y = track.y*self.track_width + 0.5*self.track_width - 0.5*self.track_space ur = snap_to_grid(vector(x,y)) - return [ll,ur] + p = pin_layout("", [ll, ur], self.get_layer(track[2])) + return p - def convert_track_to_shape(self,track): + def convert_track_to_shape_pin(self, track): """ Convert a grid point into a rectangle shape that occupies the entire centered track. """ # to scale coordinates to tracks - x = track.x*self.track_width - 0.5*self.track_width - y = track.y*self.track_width - 0.5*self.track_width + x = track[0]*self.track_width - 0.5*self.track_width + y = track[1]*self.track_width - 0.5*self.track_width + # offset lowest corner object to to (-track halo,-track halo) + ll = snap_to_grid(vector(x,y)) + ur = snap_to_grid(ll + vector(self.track_width,self.track_width)) + + p = pin_layout("", [ll, ur], self.get_layer(track[2])) + return p + + def convert_track_to_shape(self, track): + """ + Convert a grid point into a rectangle shape that occupies the entire centered + track. + """ + # to scale coordinates to tracks + x = track[0]*self.track_width - 0.5*self.track_width + y = track[1]*self.track_width - 0.5*self.track_width # offset lowest corner object to to (-track halo,-track halo) ll = snap_to_grid(vector(x,y)) ur = snap_to_grid(ll + vector(self.track_width,self.track_width)) return [ll,ur] - - def get_pin(self,pin_name): + def convert_track_to_inflated_pin(self, track): """ - Gets the pin shapes only. Doesn't add to grid. + Convert a grid point into a rectangle shape that is inflated by a half DRC space. """ - self.pins[pin_name] = self.find_pin(pin_name) - - def add_pin(self,pin_name,is_source=False): - """ - Mark the grids that are in the pin rectangle ranges to have the pin property. - pin can be a location or a label. - """ - - found_pin = False - for pin in self.pins[pin_name]: - (pin_in_tracks,blockage_in_tracks)=self.convert_pin_to_tracks(pin) - if (len(pin_in_tracks)>0): - found_pin=True - if is_source: - debug.info(1,"Set source: " + str(pin_name) + " " + str(pin_in_tracks)) - self.rg.add_source(pin_in_tracks) - else: - debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks)) - self.rg.add_target(pin_in_tracks) - self.rg.add_blockage(blockage_in_tracks) + # calculate lower left + x = track.x*self.track_width - 0.5*self.track_width - 0.5*self.track_space + y = track.y*self.track_width - 0.5*self.track_width - 0.5*self.track_space + ll = snap_to_grid(vector(x,y)) - if not found_pin: - self.write_debug_gds() - debug.check(found_pin,"Unable to find pin on grid.") + # calculate upper right + x = track.x*self.track_width + 0.5*self.track_width + 0.5*self.track_space + y = track.y*self.track_width + 0.5*self.track_width + 0.5*self.track_space + ur = snap_to_grid(vector(x,y)) - def write_debug_gds(self): + p = pin_layout("", [ll, ur], self.get_layer(track[2])) + return p + + def analyze_pins(self, pin_name): + """ + Analyze the shapes of a pin and combine them into pin_groups which are connected. + """ + debug.info(2,"Analyzing pin groups for {}.".format(pin_name)) + pin_set = self.pins[pin_name] + + # This will be a list of pin tuples that overlap + overlap_list = [] + + # Sort the rectangles into a list with lower/upper y coordinates + bottom_y_coordinates = [(x.by(), x, "bottom") for x in pin_set] + top_y_coordinates = [(x.uy(), x, "top") for x in pin_set] + y_coordinates = bottom_y_coordinates + top_y_coordinates + y_coordinates.sort(key=lambda x: x[0]) + + # Map the pins to the lower indices + bottom_index_map = {x[1]:i for i,x in enumerate(y_coordinates) if x[2]=="bottom"} + top_index_map = {x[1]:i for i,x in enumerate(y_coordinates) if x[2]=="bottom"} + + # Sort the pin list by x coordinate + pin_list = list(pin_set) + pin_list.sort(key=lambda x: x.lx()) + + # for shapes in x order + for pin in pin_list: + # start at pin's lower y coordinate + bottom_index = bottom_index_map[pin] + compared_pins = set() + for i in range(bottom_index,len(y_coordinates)): + compare_pin = y_coordinates[i][1] + # Don't overlap yourself + if pin==compare_pin: + continue + # Done when we encounter any shape above the pin + if compare_pin.by() > pin.uy(): + break + # Don't double compare the same pin twice + if compare_pin in compared_pins: + continue + compared_pins.add(compare_pin) + # If we overlap, add them to the list + if pin.overlaps(compare_pin): + overlap_list.append((pin,compare_pin)) + + # Initial unique group assignments + group_id = {} + gid = 1 + for pin in pin_list: + group_id[pin] = gid + gid += 1 + + for p in overlap_list: + (p1,p2) = p + for pin in pin_list: + if group_id[pin] == group_id[p2]: + group_id[pin] = group_id[p1] + + + # For each pin add it to it's group + group_map = {} + for pin in pin_list: + gid = group_id[pin] + if gid not in group_map.keys(): + group_map[gid] = pin_group(name=pin_name, pin_set=[], router=self) + # We always add it to the first set since they are touching + group_map[gid].pins[0].add(pin) + + self.pin_groups[pin_name] = list(group_map.values()) + + # This is the old O(n^2) implementation + # def analyze_pins(self, pin_name): + # """ + # Analyze the shapes of a pin and combine them into pin_groups which are connected. + # """ + # debug.info(2,"Analyzing pin groups for {}.".format(pin_name)) + + # pin_set = self.pins[pin_name] + + # # Put each pin in an equivalence class of it's own + # equiv_classes = [set([x]) for x in pin_set] + # def combine_classes(equiv_classes): + # for class1 in equiv_classes: + # for class2 in equiv_classes: + # if class1 == class2: + # continue + # # Compare each pin in each class, + # # and if any overlap, update equiv_classes to include the combined the class + # for p1 in class1: + # for p2 in class2: + # if p1.overlaps(p2): + # combined_class = class1 | class2 + # equiv_classes.remove(class1) + # equiv_classes.remove(class2) + # equiv_classes.append(combined_class) + # return(equiv_classes) + # return(equiv_classes) + + # old_length = math.inf + # while (len(equiv_classes)1: + newpath.append(path[-1]) + return newpath + + + + def run_router(self, detour_scale): + """ + This assumes the blockages, source, and target are all set up. + """ + # returns the path in tracks + (path,cost) = self.rg.route(detour_scale) + if path: + debug.info(2,"Found path: cost={0} ".format(cost)) + debug.info(3,str(path)) + + self.paths.append(path) + self.add_route(path) + + path_set = grid_utils.flatten_set(path) + self.path_blockages.append(path_set) + else: + self.write_debug_gds("failed_route.gds") + # clean up so we can try a reroute + self.rg.reinit() + return False + return True + + + def annotate_pin_and_tracks(self, pin, tracks): + """" + Annotate some shapes for debug purposes + """ + debug.info(0,"Annotating\n pin {0}\n tracks {1}".format(pin,tracks)) + for coord in tracks: + (ll,ur) = self.convert_track_to_shape(coord) + self.cell.add_rect(layer="text", + offset=ll, + width=ur[0]-ll[0], + height=ur[1]-ll[1]) + (ll,ur) = self.convert_track_to_pin(coord).rect + self.cell.add_rect(layer="boundary", + offset=ll, + width=ur[0]-ll[0], + height=ur[1]-ll[1]) + (ll,ur) = pin.rect + self.cell.add_rect(layer="text", + offset=ll, + width=ur[0]-ll[0], + height=ur[1]-ll[1]) + + def write_debug_gds(self, gds_name="debug_route.gds", stop_program=True): """ Write out a GDS file with the routing grid and search information annotated on it. """ - # Only add the debug info to the gds file if we have any debugging on. - # This is because we may reroute a wire with detours and don't want the debug information. - if OPTS.debug_level==0: return - + debug.info(0,"Writing annotated router gds file to {}".format(gds_name)) self.add_router_info() - debug.error("Writing debug_route.gds") - self.cell.gds_write("debug_route.gds") + self.cell.gds_write(gds_name) + + if stop_program: + import sys + sys.exit(1) + + def annotate_grid(self, g): + """ + Display grid information in the GDS file for a single grid cell. + """ + shape = self.convert_track_to_shape(g) + partial_track=vector(0,self.track_width/6.0) + self.cell.add_rect(layer="text", + offset=shape[0], + width=shape[1].x-shape[0].x, + height=shape[1].y-shape[0].y) + t=self.rg.map[g].get_type() + + # midpoint offset + off=vector((shape[1].x+shape[0].x)/2, + (shape[1].y+shape[0].y)/2) + if t!=None: + if g[2]==1: + # Upper layer is upper right label + type_off=off+partial_track + else: + # Lower layer is lower left label + type_off=off-partial_track + self.cell.add_label(text=str(t), + layer="text", + offset=type_off) + + t=self.rg.map[g].get_cost() + partial_track=vector(self.track_width/6.0,0) + if t!=None: + if g[2]==1: + # Upper layer is right label + type_off=off+partial_track + else: + # Lower layer is left label + type_off=off-partial_track + self.cell.add_label(text=str(t), + layer="text", + offset=type_off) + + self.cell.add_label(text="{0},{1}".format(g[0],g[1]), + layer="text", + offset=shape[0], + zoom=0.05) def add_router_info(self): """ @@ -436,63 +1083,56 @@ class router: called once or the labels will overlap. """ debug.info(0,"Adding router info") - grid_keys=self.rg.map.keys() - partial_track=vector(0,self.track_width/6.0) - for g in grid_keys: - shape = self.convert_track_to_shape(g) - self.cell.add_rect(layer="text", - offset=shape[0], - width=shape[1].x-shape[0].x, - height=shape[1].y-shape[0].y) - # These are the on grid pins - #rect = self.convert_track_to_pin(g) - #self.cell.add_rect(layer="boundary", - # offset=rect[0], - # width=rect[1].x-rect[0].x, - # height=rect[1].y-rect[0].y) - - t=self.rg.map[g].get_type() - - # midpoint offset - off=vector((shape[1].x+shape[0].x)/2, - (shape[1].y+shape[0].y)/2) - if g[2]==1: - # Upper layer is upper right label - type_off=off+partial_track - else: - # Lower layer is lower left label - type_off=off-partial_track - if t!=None: - self.cell.add_label(text=str(t), - layer="text", - offset=type_off) - self.cell.add_label(text="{0},{1}".format(g[0],g[1]), - layer="text", - offset=shape[0], - zoom=0.05) - - for blockage in self.blockages: - # Display the inflated blockage - (ll,ur) = blockage.inflate() - self.cell.add_rect(layer="blockage", - offset=ll, - width=ur.x-ll.x, - height=ur.y-ll.y) + show_blockages = False + show_blockage_grids = False + show_enclosures = False + show_all_grids = True + if show_all_grids: + self.rg.add_all_grids() + for g in self.rg.map.keys(): + self.annotate_grid(g) + + if show_blockages: + # Display the inflated blockage + for blockage in self.blockages: + debug.info(1,"Adding {}".format(blockage)) + (ll,ur) = blockage.inflate() + self.cell.add_rect(layer="text", + offset=ll, + width=ur.x-ll.x, + height=ur.y-ll.y) + if show_blockage_grids: + self.set_blockages(self.blocked_grids,True) + grid_keys=self.rg.map.keys() + for g in grid_keys: + self.annotate_grid(g) + + if show_enclosures: + for key in self.pin_groups.keys(): + for pg in self.pin_groups[key]: + if not pg.enclosed: + continue + for pin in pg.enclosures: + #print("enclosure: ",pin.name,pin.ll(),pin.width(),pin.height()) + self.cell.add_rect(layer="text", + offset=pin.ll(), + width=pin.width(), + height=pin.height()) + # FIXME: This should be replaced with vector.snap_to_grid at some point def snap_to_grid(offset): """ Changes the coodrinate to match the grid settings """ - grid = tech.drc["grid"] - x = offset[0] - y = offset[1] - # this gets the nearest integer value - xgrid = int(round(round((x / grid), 2), 0)) - ygrid = int(round(round((y / grid), 2), 0)) - xoff = xgrid * grid - yoff = ygrid * grid + xoff = snap_val_to_grid(offset[0]) + yoff = snap_val_to_grid(offset[1]) return vector(xoff, yoff) +def snap_val_to_grid(x): + grid = drc("grid") + xgrid = int(round(round((x / grid), 2), 0)) + xoff = xgrid * grid + return xoff diff --git a/compiler/router/router_tech.py b/compiler/router/router_tech.py new file mode 100644 index 00000000..02b6894f --- /dev/null +++ b/compiler/router/router_tech.py @@ -0,0 +1,113 @@ +from tech import drc,layer +from contact import contact +from pin_group import pin_group +from vector import vector +import debug +import math + +class router_tech: + """ + This is a class to hold the router tech constants. + """ + def __init__(self, layers, rail_track_width): + """ + Allows us to change the layers that we are routing on. First layer + is always horizontal, middle is via, and last is always + vertical. + """ + self.layers = layers + self.rail_track_width = rail_track_width + + (self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers + # This is the minimum routed track spacing + via_connect = contact(self.layers, (1, 1)) + max_via_size = max(via_connect.width,via_connect.height) + + self.horiz_layer_number = layer[self.horiz_layer_name] + self.vert_layer_number = layer[self.vert_layer_name] + + if self.rail_track_width>1: + (self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1) + (self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0) + + # For supplies, we will make the wire wider than the vias + self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size) + self.horiz_layer_minwidth = max(self.horiz_layer_minwidth, max_via_size) + + self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing + self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing + + else: + (self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1) + (self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0) + + self.horiz_track_width = max_via_size + self.horiz_layer_spacing + self.vert_track_width = max_via_size + self.vert_layer_spacing + + # We'll keep horizontal and vertical tracks the same for simplicity. + self.track_width = max(self.horiz_track_width,self.vert_track_width) + debug.info(1,"Track width: {:.3f}".format(self.track_width)) + self.track_space = max(self.horiz_layer_spacing,self.vert_layer_spacing) + debug.info(1,"Track space: {:.3f}".format(self.track_space)) + self.track_wire = self.track_width - self.track_space + debug.info(1,"Track wire width: {:.3f}".format(self.track_wire)) + + self.track_widths = vector([self.track_width] * 2) + self.track_factor = vector([1/self.track_width] * 2) + debug.info(2,"Track factor: {}".format(self.track_factor)) + + # When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side) + self.layer_widths = [self.track_wire, 1, self.track_wire] + + def get_zindex(self,layer_num): + if layer_num==self.horiz_layer_number: + return 0 + else: + return 1 + + def get_layer(self, zindex): + if zindex==1: + return self.vert_layer_name + elif zindex==0: + return self.horiz_layer_name + else: + debug.error("Invalid zindex {}".format(zindex),-1) + + def get_layer_width_space(self, zindex, width=0, length=0): + """ + Return the width and spacing of a given layer + and wire of a given width and length. + """ + if zindex==1: + layer_name = self.vert_layer_name + elif zindex==0: + layer_name = self.horiz_layer_name + else: + debug.error("Invalid zindex for track", -1) + + min_width = drc("minwidth_{0}".format(layer_name), width, length) + min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), width, length) + + return (min_width,min_spacing) + + + def get_supply_layer_width_space(self, zindex): + """ + These are the width and spacing of a supply layer given a supply rail + of the given number of min wire widths. + """ + if zindex==1: + layer_name = self.vert_layer_name + elif zindex==0: + layer_name = self.horiz_layer_name + else: + debug.error("Invalid zindex for track", -1) + + min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf) + + min_width = drc("minwidth_{0}".format(layer_name), self.rail_track_width*min_wire_width, math.inf) + min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.rail_track_width*min_wire_width, math.inf) + + return (min_width,min_spacing) + + diff --git a/compiler/router/astar_grid.py b/compiler/router/signal_grid.py similarity index 50% rename from compiler/router/astar_grid.py rename to compiler/router/signal_grid.py index 65d9e245..0f8d6315 100644 --- a/compiler/router/astar_grid.py +++ b/compiler/router/signal_grid.py @@ -1,55 +1,24 @@ from itertools import tee import debug -from vector3d import vector3d -import grid from heapq import heappush,heappop +from copy import deepcopy -class astar_grid(grid.grid): +from grid import grid +from grid_path import grid_path +from vector3d import vector3d + +class signal_grid(grid): """ Expand the two layer grid to include A* search functions for a source and target. """ - def __init__(self): + def __init__(self, ll, ur, track_factor): """ Create a routing map of width x height cells and 2 in the z-axis. """ - grid.grid.__init__(self) + grid.__init__(self, ll, ur, track_factor) - # list of the source/target grid coordinates - self.source = [] - self.target = [] - # priority queue for the maze routing self.q = [] - def set_source(self,n): - self.add_map(n) - self.map[n].source=True - self.source.append(n) - - def set_target(self,n): - self.add_map(n) - self.map[n].target=True - self.target.append(n) - - - def add_source(self,track_list): - debug.info(2,"Adding source list={0}".format(str(track_list))) - for n in track_list: - if not self.is_blocked(n): - debug.info(3,"Adding source ={0}".format(str(n))) - self.set_source(n) - - def add_target(self,track_list): - debug.info(2,"Adding target list={0}".format(str(track_list))) - for n in track_list: - if not self.is_blocked(n): - self.set_target(n) - - def is_target(self,point): - """ - Point is in the target set, so we are done. - """ - return point in self.target - def reinit(self): """ Reinitialize everything for a new route. """ @@ -73,112 +42,98 @@ class astar_grid(grid.grid): We will use an A* search, so this cost must be pessimistic. Cost so far will be the length of the path. """ - debug.info(4,"Initializing queue.") - - # uniquify the source (and target while we are at it) - self.source = list(set(self.source)) - self.target = list(set(self.target)) + #debug.info(3,"Initializing queue.") # Counter is used to not require data comparison in Python 3.x # Items will be returned in order they are added during cost ties self.counter = 0 for s in self.source: cost = self.cost_to_target(s) - debug.info(1,"Init: cost=" + str(cost) + " " + str([s])) - heappush(self.q,(cost,self.counter,[s])) + debug.info(3,"Init: cost=" + str(cost) + " " + str([s])) + heappush(self.q,(cost,self.counter,grid_path([vector3d(s)]))) self.counter+=1 def route(self,detour_scale): """ This does the A* maze routing with preferred direction routing. + This only works for 1 track wide routes! """ - + # We set a cost bound of the HPWL for run-time. This can be # over-ridden if the route fails due to pruning a feasible solution. - cost_bound = detour_scale*self.cost_to_target(self.source[0])*self.PREFERRED_COST + cost_bound = detour_scale*self.cost_to_target(self.source[0])*grid.PREFERRED_COST + # Check if something in the queue is already a source and a target! + for s in self.source: + if self.is_target(s): + return((grid_path([vector3d(s)]),0)) + # Make sure the queue is empty if we run another route while len(self.q)>0: heappop(self.q) - + # Put the source items into the queue self.init_queue() cheapest_path = None cheapest_cost = None - + + # Keep expanding and adding to the priority queue until we are done while len(self.q)>0: # should we keep the path in the queue as well or just the final node? - (cost,count,path) = heappop(self.q) - debug.info(2,"Queue size: size=" + str(len(self.q)) + " " + str(cost)) - debug.info(3,"Expanding: cost=" + str(cost) + " " + str(path)) + (cost,count,curpath) = heappop(self.q) + debug.info(3,"Queue size: size=" + str(len(self.q)) + " " + str(cost)) + debug.info(4,"Expanding: cost=" + str(cost) + " " + str(curpath)) # expand the last element - neighbors = self.expand_dirs(path) - debug.info(3,"Neighbors: " + str(neighbors)) + neighbors = self.expand_dirs(curpath) + debug.info(4,"Neighbors: " + str(neighbors)) for n in neighbors: + # make a new copy of the path to not update the old ones + newpath = deepcopy(curpath) # node is added to the map by the expand routine - newpath = path + [n] + newpath.append(n) # check if we hit the target and are done - if self.is_target(n): - return (newpath,self.cost(newpath)) - elif not self.map[n].visited: + if self.is_target(n[0]): # This uses the [0] item because we are assuming 1-track wide + return (newpath,newpath.cost()) + else: # current path cost + predicted cost - current_cost = self.cost(newpath) - target_cost = self.cost_to_target(n) + current_cost = newpath.cost() + target_cost = self.cost_to_target(n[0]) predicted_cost = current_cost + target_cost # only add the cost if it is less than our bound if (predicted_cost < cost_bound): - if (self.map[n].min_cost==-1 or current_cost=0 and not self.is_blocked(down) and not down in path: - neighbors.append(down) - - return neighbors + return unblocked_neighbors def hpwl(self, src, dest): @@ -191,7 +146,7 @@ class astar_grid(grid.grid): hpwl += max(abs(src.y-dest.y),abs(dest.y-src.y)) hpwl += max(abs(src.z-dest.z),abs(dest.z-src.z)) if src.x!=dest.x or src.y!=dest.y: - hpwl += self.VIA_COST + hpwl += grid.VIA_COST return hpwl def cost_to_target(self,source): @@ -202,37 +157,10 @@ class astar_grid(grid.grid): cost = self.hpwl(source,self.target[0]) for t in self.target: cost = min(self.hpwl(source,t),cost) - return cost - - - def cost(self,path): - """ - The cost of the path is the length plus a penalty for the number - of vias. We assume that non-preferred direction is penalized. - """ - - # Ignore the source pin layer change, FIXME? - def pairwise(iterable): - "s -> (s0,s1), (s1,s2), (s2, s3), ..." - a, b = tee(iterable) - next(b, None) - return zip(a, b) - - - plist = pairwise(path) - cost = 0 - for p0,p1 in plist: - if p0.z != p1.z: # via - cost += self.VIA_COST - elif p0.x != p1.x: # horizontal - cost += self.NONPREFERRED_COST if (p0.z == 1) else self.PREFERRED_COST - elif p0.y != p1.y: # vertical - cost += self.NONPREFERRED_COST if (p0.z == 0) else self.PREFERRED_COST - else: - debug.error("Non-changing direction!") return cost - + + def get_inertia(self,p0,p1): """ Sets the direction based on the previous direction we came from. diff --git a/compiler/router/signal_router.py b/compiler/router/signal_router.py index ebc85eb6..eb706b5b 100644 --- a/compiler/router/signal_router.py +++ b/compiler/router/signal_router.py @@ -4,24 +4,21 @@ from contact import contact import math import debug from pin_layout import pin_layout -from vector import vector -from vector3d import vector3d from globals import OPTS from router import router class signal_router(router): - """A router class to read an obstruction map from a gds and plan a + """ + A router class to read an obstruction map from a gds and plan a route on a given layer. This is limited to two layer routes. """ - def __init__(self, gds_name=None, module=None): - """Use the gds file for the blockages with the top module topName and - layers for the layers to route on + def __init__(self, layers, design, gds_filename=None): """ - router.__init__(self, gds_name, module) - - # all the paths we've routed so far (to supplement the blockages) - self.paths = [] + This will route on layers in design. It will get the blockages from + either the gds file name or the design itself (by saving to a gds file). + """ + router.__init__(self, layers, design, gds_filename) def create_routing_grid(self): @@ -33,130 +30,51 @@ class signal_router(router): size = self.ur - self.ll debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) - import astar_grid - self.rg = astar_grid.astar_grid() + import signal_grid + self.rg = signal_grid.signal_grid(self.ll, self.ur, self.track_width) - def route(self, cell, layers, src, dest, detour_scale=5): + def route(self, src, dest, detour_scale=5): """ Route a single source-destination net and return the simplified rectilinear path. Cost factor is how sub-optimal to explore for a feasible route. This is used to speed up the routing when there is not much detouring needed. """ debug.info(1,"Running signal router from {0} to {1}...".format(src,dest)) - self.cell = cell - self.source_pin_name = src - self.target_pin_name = dest + self.pins[src] = [] + self.pins[dest] = [] + # Clear the pins if we have previously routed if (hasattr(self,'rg')): self.clear_pins() else: - # Set up layers and track sizes - self.set_layers(layers) # Creat a routing grid over the entire area # FIXME: This could be created only over the routing region, # but this is simplest for now. self.create_routing_grid() - # This will get all shapes as blockages - self.find_blockages() # Get the pin shapes - self.get_pin(src) - self.get_pin(dest) + self.find_pins_and_blockages([src, dest]) - # Now add the blockages (all shapes except the src/tgt pins) - self.add_blockages() - # Add blockages from previous paths - self.add_path_blockages() - + # Block everything + self.prepare_blockages() + # Clear the pins we are routing + self.set_blockages(self.pin_components[src],False) + self.set_blockages(self.pin_components[dest],False) + # Now add the src/tgt if they are not blocked by other shapes - self.add_pin(src,True) - self.add_pin(dest,False) + self.add_source(src) + self.add_target(dest) - - # returns the path in tracks - (path,cost) = self.rg.route(detour_scale) - if path: - debug.info(1,"Found path: cost={0} ".format(cost)) - debug.info(2,str(path)) - self.add_route(path) - return True - else: - self.write_debug_gds() - # clean up so we can try a reroute - self.clear_pins() - - - return False + if not self.run_router(detour_scale=detour_scale): + self.write_debug_gds(stop_program=False) + return False + + self.write_debug_gds(stop_program=False) + return True - def add_route(self,path): - """ - Add the current wire route to the given design instance. - """ - debug.info(3,"Set path: " + str(path)) - - # Keep track of path for future blockages - self.paths.append(path) - - # This is marked for debug - self.rg.add_path(path) - - # For debugging... if the path failed to route. - if False or path==None: - self.write_debug_gds() - - - # First, simplify the path for - #debug.info(1,str(self.path)) - contracted_path = self.contract_path(path) - debug.info(1,str(contracted_path)) - - # convert the path back to absolute units from tracks - abs_path = map(self.convert_point_to_units,contracted_path) - debug.info(1,str(abs_path)) - self.cell.add_route(self.layers,abs_path) - - - def get_inertia(self,p0,p1): - """ - Sets the direction based on the previous direction we came from. - """ - # direction (index) of movement - if p0.x!=p1.x: - return 0 - elif p0.y!=p1.y: - return 1 - else: - # z direction - return 2 - - def contract_path(self,path): - """ - Remove intermediate points in a rectilinear path. - """ - newpath = [path[0]] - for i in range(1,len(path)-1): - prev_inertia=self.get_inertia(path[i-1],path[i]) - next_inertia=self.get_inertia(path[i],path[i+1]) - # if we switch directions, add the point, otherwise don't - if prev_inertia!=next_inertia: - newpath.append(path[i]) - - # always add the last path - newpath.append(path[-1]) - return newpath - - - def add_path_blockages(self): - """ - Go through all of the past paths and add them as blockages. - This is so we don't have to write/reload the GDS. - """ - for path in self.paths: - for grid in path: - self.rg.set_blocked(grid) diff --git a/compiler/router/supply_grid.py b/compiler/router/supply_grid.py index b9e1f81b..bd9dab87 100644 --- a/compiler/router/supply_grid.py +++ b/compiler/router/supply_grid.py @@ -1,27 +1,79 @@ import debug from vector3d import vector3d -import grid +from grid import grid +from signal_grid import signal_grid +from grid_path import grid_path +from direction import direction -class supply_grid(grid.grid): +class supply_grid(signal_grid): """ - A two layer routing map. Each cell can be blocked in the vertical - or horizontal layer. + This routes a supply grid. It is derived from a signal grid because it still + routes the pins to the supply rails using the same routines. + It has a few extra routines to support "waves" which are multiple track wide + directional routes (no bends). """ - def __init__(self): + def __init__(self, ll, ur, track_width): """ Create a routing map of width x height cells and 2 in the z-axis. """ - grid.grid.__init__(self) + signal_grid.__init__(self, ll, ur, track_width) - # list of the vdd/gnd rail cells - self.vdd_rails = [] - self.gnd_rails = [] - def reinit(self): """ Reinitialize everything for a new route. """ - + self.source = [] + self.target = [] # Reset all the cells in the map for p in self.map.values(): p.reset() + def find_start_wave(self, wave, width, direct): + """ + Finds the first loc starting at loc and up that is open. + Returns None if it reaches max size first. + """ + # Don't expand outside the bounding box + if wave[0].x > self.ur.x: + return None + if wave[-1].y > self.ur.y: + return None + + while wave and self.is_wave_blocked(wave): + wf=grid_path(wave) + wave=wf.neighbor(direct) + # Bail out if we couldn't increment futher + if wave[0].x > self.ur.x or wave[-1].y > self.ur.y: + return None + # Return a start if it isn't blocked + if not self.is_wave_blocked(wave): + return wave + + return wave + + + def is_wave_blocked(self, wave): + """ + Checks if any of the locations are blocked + """ + for v in wave: + if self.is_blocked(v): + return True + else: + return False + + + def probe(self, wave, direct): + """ + Expand the wave until there is a blockage and return + the wave path. + """ + wave_path = grid_path() + while wave and not self.is_wave_blocked(wave): + if wave[0].x > self.ur.x or wave[-1].y > self.ur.y: + break + wave_path.append(wave) + wave = wave_path.neighbor(direct) + + return wave_path + + diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py index 908e8686..9f2ddf57 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_router.py @@ -1,14 +1,17 @@ import gdsMill import tech -from contact import contact import math import debug -import grid +from globals import OPTS,print_time +from contact import contact +from pin_group import pin_group from pin_layout import pin_layout -from vector import vector from vector3d import vector3d -from globals import OPTS from router import router +from direction import direction +from datetime import datetime +import grid +import grid_utils class supply_router(router): """ @@ -16,131 +19,406 @@ class supply_router(router): routes a grid to connect the supply on the two layers. """ - def __init__(self, gds_name=None, module=None): - """Use the gds file for the blockages with the top module topName and - layers for the layers to route on + def __init__(self, layers, design, gds_filename=None): """ - router.__init__(self, gds_name, module) + This will route on layers in design. It will get the blockages from + either the gds file name or the design itself (by saving to a gds file). + """ + # Power rail width in minimum wire widths + self.rail_track_width = 3 - self.pins = {} + router.__init__(self, layers, design, gds_filename, self.rail_track_width) - - def clear_pins(self): - """ - Convert the routed path to blockages. - Keep the other blockages unchanged. - """ - self.pins = {} - self.rg.reinit() + # The list of supply rails (grid sets) that may be routed + self.supply_rails = {} + # This is the same as above but as a sigle set for the all the rails + self.supply_rail_tracks = {} - def route(self, cell, layers, vdd_name="vdd", gnd_name="gnd"): + + + def create_routing_grid(self): """ - Route a single source-destination net and return - the simplified rectilinear path. + Create a sprase routing grid with A* expansion functions. + """ + size = self.ur - self.ll + debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) + + import supply_grid + self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width) + + def route(self, vdd_name="vdd", gnd_name="gnd"): + """ + Add power supply rails and connect all pins to these rails. """ debug.info(1,"Running supply router on {0} and {1}...".format(vdd_name, gnd_name)) - self.cell = cell - self.pins[vdd_name] = [] - self.pins[gnd_name] = [] + self.vdd_name = vdd_name + self.gnd_name = gnd_name # Clear the pins if we have previously routed if (hasattr(self,'rg')): self.clear_pins() else: - # Set up layers and track sizes - self.set_layers(layers) # Creat a routing grid over the entire area # FIXME: This could be created only over the routing region, # but this is simplest for now. self.create_routing_grid() - # This will get all shapes as blockages - self.find_blockages() # Get the pin shapes - self.get_pin(vdd_name) - self.get_pin(gnd_name) - - # Now add the blockages (all shapes except the src/tgt pins) - self.add_blockages() - # Add blockages from previous routes - self.add_path_blockages() + start_time = datetime.now() + self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) + print_time("Finding pins and blockages",datetime.now(), start_time, 3) - # source pin will be a specific layout pin - # target pin will be the rails only + # Add the supply rails in a mesh network and connect H/V with vias + start_time = datetime.now() + # Block everything + self.prepare_blockages(self.gnd_name) + # Determine the rail locations + self.route_supply_rails(self.gnd_name,0) + + # Block everything + self.prepare_blockages(self.vdd_name) + # Determine the rail locations + self.route_supply_rails(self.vdd_name,1) + print_time("Routing supply rails",datetime.now(), start_time, 3) + + start_time = datetime.now() + self.route_simple_overlaps(vdd_name) + self.route_simple_overlaps(gnd_name) + print_time("Simple overlap routing",datetime.now(), start_time, 3) + + # Route the supply pins to the supply rails + # Route vdd first since we want it to be shorter + start_time = datetime.now() + self.route_pins_to_rails(vdd_name) + self.route_pins_to_rails(gnd_name) + print_time("Maze routing supplies",datetime.now(), start_time, 3) + #self.write_debug_gds("final.gds",False) + + return True + + + + def route_simple_overlaps(self, pin_name): + """ + This checks for simple cases where a pin component already overlaps a supply rail. + It will add an enclosure to ensure the overlap in wide DRC rule cases. + """ + debug.info(1,"Routing simple overlap pins for {0}".format(pin_name)) + + # These are the wire tracks + wire_tracks = self.supply_rail_tracks[pin_name] + routed_count=0 + for pg in self.pin_groups[pin_name]: + if pg.is_routed(): + continue - # returns the path in tracks - # (path,cost) = self.rg.route(detour_scale) - # if path: - # debug.info(1,"Found path: cost={0} ".format(cost)) - # debug.info(2,str(path)) - # self.add_route(path) - # return True - # else: - # self.write_debug_gds() - # # clean up so we can try a reroute - # self.clear_pins() + # First, check if we just overlap, if so, we are done. + overlap_grids = wire_tracks & pg.grids + if len(overlap_grids)>0: + routed_count += 1 + pg.set_routed() + continue - self.write_debug_gds() + # Else, if we overlap some of the space track, we can patch it with an enclosure + #pg.create_simple_overlap_enclosure(pg.grids) + #pg.add_enclosure(self.cell) + + debug.info(1,"Routed {} simple overlap pins".format(routed_count)) + + def finalize_supply_rails(self, name): + """ + Determine which supply rails overlap and can accomodate a via. + Remove any supply rails that do not have a via since they are disconnected. + NOTE: It is still possible though unlikely that there are disconnected groups of rails. + """ + + all_rails = self.supply_rails[name] + + connections = set() + via_areas = [] + for i1,r1 in enumerate(all_rails): + # Only consider r1 horizontal rails + e = next(iter(r1)) + if e.z==1: + continue + + # We need to move this rail to the other layer for the z indices to match + # during the intersection. This also makes a copy. + new_r1 = {vector3d(i.x,i.y,1) for i in r1} + + # If horizontal, subtract off the left/right track to prevent end of rail via + #ll = grid_utils.get_lower_left(new_r1) + #ur = grid_utils.get_upper_right(new_r1) + grid_utils.remove_border(new_r1, direction.EAST) + grid_utils.remove_border(new_r1, direction.WEST) + + for i2,r2 in enumerate(all_rails): + # Never compare to yourself + if i1==i2: + continue + + # Only consider r2 vertical rails + e = next(iter(r2)) + if e.z==0: + continue + + # Need to maek a copy to consider via overlaps to ignore the end-caps + new_r2 = r2.copy() + grid_utils.remove_border(new_r2, direction.NORTH) + grid_utils.remove_border(new_r2, direction.SOUTH) + + # Determine if we hhave sufficient overlap and, if so, + # remember: + # the indices to determine a rail is connected to another + # the overlap area for placement of a via + overlap = new_r1 & new_r2 + if len(overlap) >= 1: + debug.info(3,"Via overlap {0} {1}".format(len(overlap),overlap)) + connections.update([i1,i2]) + via_areas.append(overlap) + + # Go through and add the vias at the center of the intersection + for area in via_areas: + ll = grid_utils.get_lower_left(area) + ur = grid_utils.get_upper_right(area) + center = (ll + ur).scale(0.5,0.5,0) + self.add_via(center,1) + + # Determien which indices were not connected to anything above + missing_indices = set([x for x in range(len(self.supply_rails[name]))]) + missing_indices.difference_update(connections) + + # Go through and remove those disconnected indices + # (No via was added, so that doesn't need to be removed) + for rail_index in sorted(missing_indices, reverse=True): + ll = grid_utils.get_lower_left(all_rails[rail_index]) + ur = grid_utils.get_upper_right(all_rails[rail_index]) + debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur)) + self.supply_rails[name].pop(rail_index) + + # Make the supply rails into a big giant set of grids for easy blockages. + # Must be done after we determine which ones are connected. + self.create_supply_track_set(name) + + + def add_supply_rails(self, name): + """ + Add the shapes that represent the routed supply rails. + This is after the paths have been pruned and only include rails that are + connected with vias. + """ + for rail in self.supply_rails[name]: + ll = grid_utils.get_lower_left(rail) + ur = grid_utils.get_upper_right(rail) + z = ll.z + pin = self.compute_pin_enclosure(ll, ur, z, name) + debug.info(2,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin)) + self.cell.add_layout_pin(text=name, + layer=pin.layer, + offset=pin.ll(), + width=pin.width(), + height=pin.height()) + + def compute_supply_rails(self, name, supply_number): + """ + Compute the unblocked locations for the horizontal and vertical supply rails. + Go in a raster order from bottom to the top (for horizontal) and left to right + (for vertical). Start with an initial start_offset in x and y direction. + """ + + self.supply_rails[name]=[] + + max_yoffset = self.rg.ur.y + max_xoffset = self.rg.ur.x + min_yoffset = self.rg.ll.y + min_xoffset = self.rg.ll.x + + + # Horizontal supply rails + start_offset = min_yoffset + supply_number + for offset in range(start_offset, max_yoffset, 2): + # Seed the function at the location with the given width + wave = [vector3d(min_xoffset,offset,0)] + # While we can keep expanding east in this horizontal track + while wave and wave[0].x < max_xoffset: + added_rail = self.find_supply_rail(name, wave, direction.EAST) + if not added_rail: + # Just seed with the next one + wave = [x+vector3d(1,0,0) for x in wave] + else: + # Seed with the neighbor of the end of the last rail + wave = added_rail.neighbor(direction.EAST) + + # Vertical supply rails + start_offset = min_xoffset + supply_number + for offset in range(start_offset, max_xoffset, 2): + # Seed the function at the location with the given width + wave = [vector3d(offset,min_yoffset,1)] + # While we can keep expanding north in this vertical track + while wave and wave[0].y < max_yoffset: + added_rail = self.find_supply_rail(name, wave, direction.NORTH) + if not added_rail: + # Just seed with the next one + wave = [x+vector3d(0,1,0) for x in wave] + else: + # Seed with the neighbor of the end of the last rail + wave = added_rail.neighbor(direction.NORTH) + + def find_supply_rail(self, name, seed_wave, direct): + """ + Find a start location, probe in the direction, and see if the rail is big enough + to contain a via, and, if so, add it. + """ + start_wave = self.find_supply_rail_start(name, seed_wave, direct) + + # This means there were no more unblocked grids in the row/col + if not start_wave: + return None + + wave_path = self.probe_supply_rail(name, start_wave, direct) + + self.approve_supply_rail(name, wave_path) + + # Return the rail whether we approved it or not, + # as it will be used to find the next start location + return wave_path + + def find_supply_rail_start(self, name, seed_wave, direct): + """ + This finds the first valid starting location and routes a supply rail + in the given direction. + It returns the space after the end of the rail to seed another call for multiple + supply rails in the same "track" when there is a blockage. + """ + # Sweep to find an initial unblocked valid wave + start_wave = self.rg.find_start_wave(seed_wave, len(seed_wave), direct) + + return start_wave + + def probe_supply_rail(self, name, start_wave, direct): + """ + This finds the first valid starting location and routes a supply rail + in the given direction. + It returns the space after the end of the rail to seed another call for multiple + supply rails in the same "track" when there is a blockage. + """ + + # Expand the wave to the right + wave_path = self.rg.probe(start_wave, direct) + + if not wave_path: + return None + + # drop the first and last steps to leave escape routing room + # around the blockage that stopped the probe + # except, don't drop the first if it is the first in a row/column + if (direct==direction.NORTH and start_wave[0].y>0): + wave_path.trim_first() + elif (direct == direction.EAST and start_wave[0].x>0): + wave_path.trim_first() + + wave_path.trim_last() + + return wave_path + + def approve_supply_rail(self, name, wave_path): + """ + Check if the supply rail is sufficient (big enough) and add it to the + data structure. Return whether it was added or not. + """ + # We must have at least 2 tracks to drop plus 2 tracks for a via + if len(wave_path)>=4*self.rail_track_width: + grid_set = wave_path.get_grids() + self.supply_rails[name].append(grid_set) + return True + return False - - def add_route(self,path): - """ - Add the current wire route to the given design instance. + + + + + def route_supply_rails(self, name, supply_number): """ - debug.info(3,"Set path: " + str(path)) + Route the horizontal and vertical supply rails across the entire design. + Must be done with lower left at 0,0 + """ + debug.info(1,"Routing supply rail {0}.".format(name)) - # Keep track of path for future blockages - self.paths.append(path) + # Compute the grid locations of the supply rails + self.compute_supply_rails(name, supply_number) - # This is marked for debug - self.rg.add_path(path) + # Add the supply rail vias (and prune disconnected rails) + self.finalize_supply_rails(name) - # For debugging... if the path failed to route. - if False or path==None: - self.write_debug_gds() + # Add the rails themselves + self.add_supply_rails(name) - # First, simplify the path for - #debug.info(1,str(self.path)) - contracted_path = self.contract_path(path) - debug.info(1,str(contracted_path)) + + def create_supply_track_set(self, pin_name): + """ + Make a single set of all the tracks for the rail and wire itself. + """ + rail_set = set() + for rail in self.supply_rails[pin_name]: + rail_set.update(rail) + self.supply_rail_tracks[pin_name] = rail_set - # convert the path back to absolute units from tracks - abs_path = map(self.convert_point_to_units,contracted_path) - debug.info(1,str(abs_path)) - self.cell.add_route(self.layers,abs_path) + + + def route_pins_to_rails(self, pin_name): + """ + This will route each of the remaining pin components to the supply rails. + After it is done, the cells are added to the pin blockage list. + """ + + remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name]) + debug.info(1,"Maze routing {0} with {1} pin components to connect.".format(pin_name, + remaining_components)) + + for index,pg in enumerate(self.pin_groups[pin_name]): + if pg.is_routed(): + continue + + debug.info(3,"Routing component {0} {1}".format(pin_name, index)) + + # Clear everything in the routing grid. + self.rg.reinit() + + # This is inefficient since it is non-incremental, but it was + # easier to debug. + self.prepare_blockages(pin_name) + + # Add the single component of the pin as the source + # which unmarks it as a blockage too + self.add_pin_component_source(pin_name,index) + + # Add all of the rails as targets + # Don't add the other pins, but we could? + self.add_supply_rail_target(pin_name) + + # Actually run the A* router + if not self.run_router(detour_scale=5): + self.write_debug_gds() - def create_routing_grid(self): - """ - Create a sprase routing grid with A* expansion functions. + def add_supply_rail_target(self, pin_name): """ - # We will add a halo around the boundary - # of this many tracks - size = self.ur - self.ll - debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) + Add the supply rails of given name as a routing target. + """ + debug.info(4,"Add supply rail target {}".format(pin_name)) + # Add the wire itself as the target + self.rg.set_target(self.supply_rail_tracks[pin_name]) + # But unblock all the rail tracks including the space + self.rg.set_blocked(self.supply_rail_tracks[pin_name],False) - import supply_grid - self.rg = supply_grid.supply_grid() - - - ########################## - # Gridded supply route functions - ########################## - def create_grid(self, ll, ur): - """ Create alternating vdd/gnd lines horizontally """ - - self.create_horizontal_grid() - self.create_vertical_grid() - - - def create_horizontal_grid(self): - """ Create alternating vdd/gnd lines horizontally """ - - pass - - def create_vertical_grid(self): - """ Create alternating vdd/gnd lines horizontally """ - pass - + + def set_supply_rail_blocked(self, value=True): + """ + Add the supply rails of given name as a routing target. + """ + debug.info(4,"Blocking supply rail") + for rail_name in self.supply_rail_tracks: + self.rg.set_blocked(self.supply_rail_tracks[rail_name]) + diff --git a/compiler/router/tests/01_no_blockages_test.py b/compiler/router/tests/01_no_blockages_test.py index c7344a64..4197f714 100755 --- a/compiler/router/tests/01_no_blockages_test.py +++ b/compiler/router/tests/01_no_blockages_test.py @@ -37,9 +37,9 @@ class no_blockages_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") - self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) + r=router(layer_stack,self,gds_file) + self.assertTrue(r.route(src="A",dest="B")) r=routing("01_no_blockages_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) diff --git a/compiler/router/tests/01_no_blockages_test_scn3me_subm.gds b/compiler/router/tests/01_no_blockages_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/01_no_blockages_test_scn3me_subm.gds rename to compiler/router/tests/01_no_blockages_test_scn4m_subm.gds diff --git a/compiler/router/tests/02_blockages_test.py b/compiler/router/tests/02_blockages_test.py index 2e85b1c2..6e3bee08 100755 --- a/compiler/router/tests/02_blockages_test.py +++ b/compiler/router/tests/02_blockages_test.py @@ -37,9 +37,9 @@ class blockages_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") - self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) + r=router(layer_stack,self,gds_file) + self.assertTrue(r.route(src="A",dest="B")) r=routing("02_blockages_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) diff --git a/compiler/router/tests/02_blockages_test_scn3me_subm.gds b/compiler/router/tests/02_blockages_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/02_blockages_test_scn3me_subm.gds rename to compiler/router/tests/02_blockages_test_scn4m_subm.gds diff --git a/compiler/router/tests/03_same_layer_pins_test.py b/compiler/router/tests/03_same_layer_pins_test.py index 98ce3a2a..726cd02b 100755 --- a/compiler/router/tests/03_same_layer_pins_test.py +++ b/compiler/router/tests/03_same_layer_pins_test.py @@ -36,9 +36,9 @@ class same_layer_pins_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") - self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) + r=router(layer_stack,self,gds_file) + self.assertTrue(r.route(src="A",dest="B")) r = routing("03_same_layer_pins_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) diff --git a/compiler/router/tests/03_same_layer_pins_test_scn3me_subm.gds b/compiler/router/tests/03_same_layer_pins_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/03_same_layer_pins_test_scn3me_subm.gds rename to compiler/router/tests/03_same_layer_pins_test_scn4m_subm.gds diff --git a/compiler/router/tests/04_diff_layer_pins_test.py b/compiler/router/tests/04_diff_layer_pins_test.py index cbc21470..2882156f 100755 --- a/compiler/router/tests/04_diff_layer_pins_test.py +++ b/compiler/router/tests/04_diff_layer_pins_test.py @@ -38,9 +38,9 @@ class diff_layer_pins_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") - self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) + r=router(layer_stack,self,gds_file) + self.assertTrue(r.route(src="A",dest="B")) r = routing("04_diff_layer_pins_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) diff --git a/compiler/router/tests/04_diff_layer_pins_test_scn3me_subm.gds b/compiler/router/tests/04_diff_layer_pins_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/04_diff_layer_pins_test_scn3me_subm.gds rename to compiler/router/tests/04_diff_layer_pins_test_scn4m_subm.gds diff --git a/compiler/router/tests/05_two_nets_test.py b/compiler/router/tests/05_two_nets_test.py index 166292d0..e71920a8 100755 --- a/compiler/router/tests/05_two_nets_test.py +++ b/compiler/router/tests/05_two_nets_test.py @@ -38,10 +38,10 @@ class two_nets_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") - self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) - self.assertTrue(r.route(self,layer_stack,src="C",dest="D")) + r=router(layer_stack,self,gds_file) + self.assertTrue(r.route(src="A",dest="B")) + self.assertTrue(r.route(src="C",dest="D")) r = routing("05_two_nets_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) diff --git a/compiler/router/tests/05_two_nets_test_scn3me_subm.gds b/compiler/router/tests/05_two_nets_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/05_two_nets_test_scn3me_subm.gds rename to compiler/router/tests/05_two_nets_test_scn4m_subm.gds diff --git a/compiler/router/tests/06_pin_location_test.py b/compiler/router/tests/06_pin_location_test.py index e67fed53..f469d326 100755 --- a/compiler/router/tests/06_pin_location_test.py +++ b/compiler/router/tests/06_pin_location_test.py @@ -37,13 +37,13 @@ class pin_location_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") + r=router(layer_stack,self,gds_file) # these are user coordinates and layers src_pin = [[0.52, 4.099],11] tgt_pin = [[3.533, 1.087],11] #r.route(layer_stack,src="A",dest="B") - self.assertTrue(r.route(self,layer_stack,src=src_pin,dest=tgt_pin)) + self.assertTrue(r.route(src=src_pin,dest=tgt_pin)) # This only works for freepdk45 since the coordinates are hard coded if OPTS.tech_name == "freepdk45": diff --git a/compiler/router/tests/07_big_test.py b/compiler/router/tests/07_big_test.py index 5f844ec5..8fcf2826 100755 --- a/compiler/router/tests/07_big_test.py +++ b/compiler/router/tests/07_big_test.py @@ -37,8 +37,8 @@ class big_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") + r=router(layer_stack,self,gds_file) connections=[('out_0_2', 'a_0_0'), ('out_0_3', 'b_0_0'), ('out_0_0', 'a_0_1'), @@ -61,7 +61,7 @@ class big_test(openram_test): ('out_4_1', 'a_4_3'), ('out_4_5', 'b_4_3')] for (src,tgt) in connections: - self.assertTrue(r.route(self,layer_stack,src=src,dest=tgt)) + self.assertTrue(r.route(src=src,dest=tgt)) # This test only runs on scn3me_subm tech if OPTS.tech_name=="scn3me_subm": diff --git a/compiler/router/tests/07_big_test_scn3me_subm.gds b/compiler/router/tests/07_big_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/07_big_test_scn3me_subm.gds rename to compiler/router/tests/07_big_test_scn4m_subm.gds diff --git a/compiler/router/tests/08_expand_region_test.py b/compiler/router/tests/08_expand_region_test.py index 96edab57..3e63bd51 100755 --- a/compiler/router/tests/08_expand_region_test.py +++ b/compiler/router/tests/08_expand_region_test.py @@ -37,12 +37,12 @@ class expand_region_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") + r=router(layer_stack,self,gds_file) # This should be infeasible because it is blocked without a detour. - self.assertFalse(r.route(self,layer_stack,src="A",dest="B",detour_scale=1)) + self.assertFalse(r.route(src="A",dest="B",detour_scale=1)) # This should be feasible because we allow it to detour - self.assertTrue(r.route(self,layer_stack,src="A",dest="B",detour_scale=3)) + self.assertTrue(r.route(src="A",dest="B",detour_scale=3)) r = routing("08_expand_region_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) diff --git a/compiler/router/tests/08_expand_region_test_scn3me_subm.gds b/compiler/router/tests/08_expand_region_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/08_expand_region_test_scn3me_subm.gds rename to compiler/router/tests/08_expand_region_test_scn4m_subm.gds diff --git a/compiler/router/tests/10_supply_grid_test.py b/compiler/router/tests/10_supply_grid_test.py index 2fa5cb7b..7258ab40 100755 --- a/compiler/router/tests/10_supply_grid_test.py +++ b/compiler/router/tests/10_supply_grid_test.py @@ -17,36 +17,26 @@ class no_blockages_test(openram_test): def runTest(self): globals.init_openram("config_{0}".format(OPTS.tech_name)) - from gds_cell import gds_cell - from design import design from supply_router import supply_router as router - class routing(design, openram_test): - """ - A generic GDS design that we can route on. - """ - def __init__(self, name): - design.__init__(self, "top") + if False: + from control_logic import control_logic + cell = control_logic(16) + layer_stack =("metal3","via3","metal4") + rtr=router(layer_stack, cell) + self.assertTrue(rtr.route()) + else: + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + + c.words_per_row=1 + sram = sram(c, "sram1") + cell = sram.s - # Instantiate a GDS cell with the design - globals.setup_paths() - from control_logic import control_logic - cell = control_logic(16) - #from pinv import pinv - #cell = pinv() - #gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),"control_logic") - #cell = gds_cell(name, gds_file) - self.add_inst(name=name, - mod=cell, - offset=[0,0]) - self.connect_inst(cell.pin_map.keys()) - - r=router(module=cell) - layer_stack =("metal3","via2","metal2") - self.assertTrue(r.route(self,layer_stack)) - - r=routing("10_supply_grid_test_{0}".format(OPTS.tech_name)) - self.local_drc_check(r) + self.local_check(cell,True) # fails if there are any DRC errors on any cells globals.end_openram() diff --git a/compiler/router/tests/10_supply_grid_test_scn3me_subm.gds b/compiler/router/tests/10_supply_grid_test_scn3me_subm.gds deleted file mode 100644 index 7cfbc0cb..00000000 Binary files a/compiler/router/tests/10_supply_grid_test_scn3me_subm.gds and /dev/null differ diff --git a/compiler/router/tests/config_scn3me_subm.py b/compiler/router/tests/config_scn4m_subm.py similarity index 79% rename from compiler/router/tests/config_scn3me_subm.py rename to compiler/router/tests/config_scn4m_subm.py index 330d463b..e3aa1498 100755 --- a/compiler/router/tests/config_scn3me_subm.py +++ b/compiler/router/tests/config_scn4m_subm.py @@ -1,7 +1,7 @@ word_size = 1 num_words = 16 -tech_name = "scn3me_subm" +tech_name = "scn4m_subm" process_corners = ["TT"] supply_voltages = [5.0] temperatures = [25] diff --git a/compiler/router/tests/regress.py b/compiler/router/tests/regress.py index 02c077f1..b40263c7 100755 --- a/compiler/router/tests/regress.py +++ b/compiler/router/tests/regress.py @@ -4,7 +4,6 @@ import re import unittest import sys,os sys.path.append(os.path.join(sys.path[0],"../../compiler")) -print(sys.path) import globals (OPTS, args) = globals.parse_args() diff --git a/compiler/router/vector3d.py b/compiler/router/vector3d.py index b84f2eda..1d0d083e 100644 --- a/compiler/router/vector3d.py +++ b/compiler/router/vector3d.py @@ -15,20 +15,20 @@ class vector3d(): self.x = x[0] self.y = x[1] self.z = x[2] - #will take two inputs as the values of a coordinate + #will take inputs as the values of a coordinate else: self.x = x self.y = y self.z = z - self.tpl=(x,y,z) + def __str__(self): """ override print function output """ - return "vector3d:["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" + return "v3d["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" def __repr__(self): """ override print function output """ - return "["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" + return "v3d["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" def __setitem__(self, index, value): """ @@ -89,7 +89,7 @@ class vector3d(): Note: This assumes that you DON'T CHANGE THE VECTOR or it will break things. """ - return hash(self.tpl) + return hash((self.x,self.y,self.z)) def __rsub__(self, other): @@ -118,12 +118,39 @@ class vector3d(): x_factor=x_factor[0] return vector3d(self.y*x_factor,self.x*y_factor,self.z*z_factor) + def floor(self): + """ + Override floor function + """ + return vector3d(int(math.floor(self.x)),int(math.floor(self.y)), self.z) + + def ceil(self): + """ + Override ceil function + """ + return vector3d(int(math.ceil(self.x)),int(math.ceil(self.y)), self.z) + + def round(self): + """ + Override round function + """ + return vector3d(int(round(self.x)),int(round(self.y)), self.z) + def __eq__(self, other): """Override the default Equals behavior""" if isinstance(other, self.__class__): - return self.__dict__ == other.__dict__ + return self.x==other.x and self.y==other.y and self.z==other.z return False + def __lt__(self, other): + """Override the default less than behavior""" + if isinstance(other, self.__class__): + if self.x1: + # Port 1 + port = 1 + + # This includes 2 M2 pitches for the row addr clock line + # It is also placed to align with the column decoder (if it exists hence the bank gap) + control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch, + self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y + self.bank.m2_gap) + self.control_logic_insts[port].place(control_pos[port], mirror="MY") - # two supply rails are already included in the bank, so just 2 here. - # self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch - # self.height = self.bank.height + # The row address bits are placed above the control logic aligned on the left. + x_offset = control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width + # It is above the control logic but below the top of the bitcell array + y_offset = max(self.control_logic_insts[port].uy(), self.bank.bank_array_ur.y - self.row_addr_dff_insts[port].height) + row_addr_pos[port] = vector(x_offset, y_offset) + self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="MY") + # Add the col address flops above the bank to the right of the upper-right of bank array + if self.col_addr_dff: + col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, + self.bank.height + max_gap_size + self.col_addr_dff_insts[port].height) + self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX") + + # Add the data flops above the bank to the left of the upper-right of bank array + # This relies on the upper-right of the array of the bank + # decoder in upper left, bank in upper right, sensing in lower right. + # These flops go below the sensing and leave a gap to channel route to the + # sense amps. + if port in self.write_ports: + data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, + self.bank.height + max_gap_size + self.data_dff_insts[port].height) + self.data_dff_insts[port].place(data_pos[port], mirror="MX") + + def add_layout_pins(self): """ Add the top-level pins for a single bank SRAM with control. """ - # Connect the control pins as inputs - for n in self.control_logic_inputs + ["clk"]: - self.copy_layout_pin(self.control_logic_inst[0], n) + for port in self.all_ports: + # Connect the control pins as inputs + for signal in self.control_logic_inputs[port] + ["clk"]: + self.copy_layout_pin(self.control_logic_insts[port], signal, signal+"{}".format(port)) - for i in range(self.word_size): - dout_name = "dout0[{}]".format(i) - self.copy_layout_pin(self.bank_inst, dout_name, "DOUT0[{}]".format(i)) + if port in self.read_ports: + for bit in range(self.word_size): + self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port,bit), "DOUT{0}[{1}]".format(port,bit)) - # Lower address bits - for i in range(self.col_addr_size): - self.copy_layout_pin(self.col_addr_dff_inst, "din[{}]".format(i),"ADDR0[{}]".format(i)) - # Upper address bits - for i in range(self.row_addr_size): - self.copy_layout_pin(self.row_addr_dff_inst, "din[{}]".format(i),"ADDR0[{}]".format(i+self.col_addr_size)) + # Lower address bits + for bit in range(self.col_addr_size): + self.copy_layout_pin(self.col_addr_dff_insts[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit)) + # Upper address bits + for bit in range(self.row_addr_size): + self.copy_layout_pin(self.row_addr_dff_insts[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit+self.col_addr_size)) - for i in range(self.word_size): - din_name = "din[{}]".format(i) - self.copy_layout_pin(self.data_dff_inst, din_name, "DIN0[{}]".format(i)) + if port in self.write_ports: + for bit in range(self.word_size): + self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit)) - def route(self): + def route_layout(self): """ Route a single bank SRAM """ self.add_layout_pins() - self.route_vdd_gnd() - self.route_clk() self.route_control_logic() @@ -136,195 +173,123 @@ class sram_1bank(sram_base): """ Route the clock network """ # This is the actual input to the SRAM - self.copy_layout_pin(self.control_logic_inst[0], "clk") + for port in self.all_ports: + self.copy_layout_pin(self.control_logic_insts[port], "clk", "clk{}".format(port)) - # Connect all of these clock pins to the clock in the central bus - # This is something like a "spine" clock distribution. The two spines - # are clk_buf and clk_buf_bar - - bank_clk_buf_pin = self.bank_inst.get_pin("clk_buf") - bank_clk_buf_pos = bank_clk_buf_pin.center() - bank_clk_buf_bar_pin = self.bank_inst.get_pin("clk_buf_bar") - bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center() - - if self.col_addr_dff: - dff_clk_pin = self.col_addr_dff_inst.get_pin("clk") - dff_clk_pos = dff_clk_pin.center() - mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y) - self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos]) - - data_dff_clk_pin = self.data_dff_inst.get_pin("clk") - data_dff_clk_pos = data_dff_clk_pin.center() - mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y) - self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos]) - - # This uses a metal2 track to the right of the control/row addr DFF - # to route vertically. - control_clk_buf_pin = self.control_logic_inst[0].get_pin("clk_buf") - control_clk_buf_pos = control_clk_buf_pin.rc() - row_addr_clk_pin = self.row_addr_dff_inst.get_pin("clk") - row_addr_clk_pos = row_addr_clk_pin.rc() - mid1_pos = vector(self.row_addr_dff_inst.rx() + self.m2_pitch, - row_addr_clk_pos.y) - mid2_pos = vector(mid1_pos.x, - control_clk_buf_pos.y) - # Note, the via to the control logic is taken care of when we route - # the control logic to the bank - self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, mid2_pos, control_clk_buf_pos]) - - def route_vdd_gnd(self): - """ Propagate all vdd/gnd pins up to this level for all modules """ - - # These are the instances that every bank has - top_instances = [self.bank_inst, - self.row_addr_dff_inst, - self.data_dff_inst, - self.control_logic_inst[0]] - if self.col_addr_dff: - top_instances.append(self.col_addr_dff_inst) - - - for inst in top_instances: - self.copy_layout_pin(inst, "vdd") - self.copy_layout_pin(inst, "gnd") - - def new_route_vdd_gnd(self): - """ Propagate all vdd/gnd pins up to this level for all modules """ - - # These are the instances that every bank has - top_instances = [self.bank_inst, - self.row_addr_dff_inst, - self.data_dff_inst, - self.control_logic_inst[0]] - if self.col_addr_dff: - top_instances.append(self.col_addr_dff_inst) - - - # for inst in top_instances: - # self.copy_layout_pin(inst, "vdd") - # self.copy_layout_pin(inst, "gnd") - - blockages=self.get_blockages("metal3", top_level=True) - - # Gather all of the vdd/gnd pins - vdd_pins=[] - gnd_pins=[] - for inst in top_instances: - vdd_pins.extend([x for x in inst.get_pins("vdd") if x.layer == "metal3"]) - gnd_pins.extend([x for x in inst.get_pins("gnd") if x.layer == "metal3"]) - - # Create candidate stripes on M3/M4 - lowest=self.find_lowest_coords() - highest=self.find_highest_coords() - m3_y_coords = np.arange(lowest[1],highest[1],self.m2_pitch) - - # These are the rails that will be available for vdd/gnd - m3_rects = [] - # These are the "inflated" shapes for DRC checks - m3_drc_rects = [] - for y in m3_y_coords: - # This is just what metal will be drawn - ll = vector(lowest[0], y - 0.5*self.m3_width) - ur = vector(highest[0], y + 0.5*self.m3_width) - m3_rects.append([ll, ur]) - # This is a full m3 pitch for DRC conflict checking - ll = vector(lowest[0], y - 0.5*self.m3_pitch ) - ur = vector(highest[0], y + 0.5*self.m3_pitch) - m3_drc_rects.append([ll, ur]) - - vdd_rects = [] - gnd_rects = [] - - # Now, figure how if the rails intersect a blockage, vdd, or gnd pin - # Divide the rails up alternately - # This should be done in less than n^2 using a kd-tree or something - # for drc_rect,rect in zip(m3_drc_rects,m3_rects): - # for b in blockages: - # if rect_overlaps(b,drc_rect): - # break - # else: - # gnd_rects.append(rect) - - - - # Create the vdd and gnd rails - for rect in m3_rects: - (ll,ur) = rect + # Connect all of these clock pins to the clock in the central bus + # This is something like a "spine" clock distribution. The two spines + # are clk_buf and clk_buf_bar + control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf") + control_clk_buf_pos = control_clk_buf_pin.center() - for rect in gnd_rects: - (ll,ur) = rect - self.add_layout_pin(text="gnd", - layer="metal3", - offset=ll, - width=ur.x-ll.x, - height=ur.y-ll.y) - for rect in vdd_rects: - (ll,ur) = rect - self.add_layout_pin(text="vdd", - layer="metal3", - offset=ll, - width=ur.x-ll.x, - height=ur.y-ll.y) + # This uses a metal2 track to the right (for port0) of the control/row addr DFF + # to route vertically. For port1, it is to the left. + row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk") + if port%2: + control_clk_buf_pos = control_clk_buf_pin.lc() + row_addr_clk_pos = row_addr_clk_pin.lc() + mid1_pos = vector(self.row_addr_dff_insts[port].lx() - self.m2_pitch, + row_addr_clk_pos.y) + else: + control_clk_buf_pos = control_clk_buf_pin.rc() + row_addr_clk_pos = row_addr_clk_pin.rc() + mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch, + row_addr_clk_pos.y) + + # This is the steiner point where the net branches out + clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y) + self.add_path("metal1", [control_clk_buf_pos, clk_steiner_pos]) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=clk_steiner_pos, + rotate=90) + + # Note, the via to the control logic is taken care of above + self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos]) + + if self.col_addr_dff: + dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk") + dff_clk_pos = dff_clk_pin.center() + mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y) + self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, clk_steiner_pos]) + + if port in self.write_ports: + data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") + data_dff_clk_pos = data_dff_clk_pin.center() + mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y) + # In some designs, the steiner via will be too close to the mid_pos via + # so make the wire as wide as the contacts + self.add_path("metal2",[mid_pos, clk_steiner_pos], width=max(m2m3.width,m2m3.height)) + self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos]) + def route_control_logic(self): """ Route the outputs from the control logic module """ - for n in self.control_logic_outputs: - src_pin = self.control_logic_inst[0].get_pin(n) - dest_pin = self.bank_inst.get_pin(n) - self.connect_rail_from_left_m2m3(src_pin, dest_pin) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=src_pin.rc(), - rotate=90) + for port in self.all_ports: + for signal in self.control_logic_outputs[port]: + # The clock gets routed separately and is not a part of the bank + if "clk" in signal: + continue + src_pin = self.control_logic_insts[port].get_pin(signal) + dest_pin = self.bank_inst.get_pin(signal+"{}".format(port)) + self.connect_rail_from_left_m2m3(src_pin, dest_pin) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=src_pin.rc(), + rotate=90) def route_row_addr_dff(self): """ Connect the output of the row flops to the bank pins """ - for i in range(self.row_addr_size): - flop_name = "dout[{}]".format(i) - bank_name = "addr0[{}]".format(i+self.col_addr_size) - flop_pin = self.row_addr_dff_inst.get_pin(flop_name) - bank_pin = self.bank_inst.get_pin(bank_name) - flop_pos = flop_pin.center() - bank_pos = bank_pin.center() - mid_pos = vector(bank_pos.x,flop_pos.y) - self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos,bank_pos]) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=flop_pos, - rotate=90) + for port in self.all_ports: + for bit in range(self.row_addr_size): + flop_name = "dout_{}".format(bit) + bank_name = "addr{0}_{1}".format(port,bit+self.col_addr_size) + flop_pin = self.row_addr_dff_insts[port].get_pin(flop_name) + bank_pin = self.bank_inst.get_pin(bank_name) + flop_pos = flop_pin.center() + bank_pos = bank_pin.center() + mid_pos = vector(bank_pos.x,flop_pos.y) + self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos,bank_pos]) + self.add_via_center(layers=("metal2","via2","metal3"), + offset=flop_pos, + rotate=90) def route_col_addr_dff(self): """ Connect the output of the row flops to the bank pins """ + for port in self.all_ports: + bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)] + col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1", + pitch=self.m1_pitch, + offset=self.col_addr_dff_insts[port].ul() + vector(0, self.m1_pitch), + names=bus_names, + length=self.col_addr_dff_insts[port].width) - bus_names = ["addr[{}]".format(x) for x in range(self.col_addr_size)] - col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1", - pitch=self.m1_pitch, - offset=self.col_addr_dff_inst.ul() + vector(0, self.m1_pitch), - names=bus_names, - length=self.col_addr_dff_inst.width) - - dff_names = ["dout[{}]".format(x) for x in range(self.col_addr_size)] - data_dff_map = zip(dff_names, bus_names) - self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_inst, col_addr_bus_offsets) - - bank_names = ["addr0[{}]".format(x) for x in range(self.col_addr_size)] - data_bank_map = zip(bank_names, bus_names) - self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets) + dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)] + data_dff_map = zip(dff_names, bus_names) + self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_insts[port], col_addr_bus_offsets) + + bank_names = ["addr{0}_{1}".format(port,x) for x in range(self.col_addr_size)] + data_bank_map = zip(bank_names, bus_names) + self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets) def route_data_dff(self): """ Connect the output of the data flops to the write driver """ # This is where the channel will start (y-dimension at least) - offset = self.data_dff_inst.ul() + vector(0, self.m1_pitch) + for port in self.write_ports: + if port%2: + offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch) + else: + offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) - dff_names = ["dout[{}]".format(x) for x in range(self.word_size)] - bank_names = ["din0[{}]".format(x) for x in range(self.word_size)] - route_map = list(zip(bank_names, dff_names)) - dff_pins = {key: self.data_dff_inst.get_pin(key) for key in dff_names } - bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names } - # Combine the dff and bank pins into a single dictionary of pin name to pin. - all_pins = {**dff_pins, **bank_pins} - self.create_horizontal_channel_route(route_map, all_pins, offset) + dff_names = ["dout_{}".format(x) for x in range(self.word_size)] + dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] + + bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)] + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + + route_map = list(zip(bank_pins, dff_pins)) + self.create_horizontal_channel_route(route_map, offset) @@ -335,8 +300,8 @@ class sram_1bank(sram_base): will show these as ports in the extracted netlist. """ - for n in self.control_logic_outputs: - pin = self.control_logic_inst[0].get_pin(n) + for n in self.control_logic_outputs[0]: + pin = self.control_logic_insts[0].get_pin(n) self.add_label(text=n, layer=pin.layer, offset=pin.center()) diff --git a/compiler/sram_4bank.py b/compiler/sram_4bank.py deleted file mode 100644 index 44b8ba87..00000000 --- a/compiler/sram_4bank.py +++ /dev/null @@ -1,331 +0,0 @@ -import sys -from tech import drc, spice -import debug -from math import log,sqrt,ceil -import datetime -import getpass -from vector import vector -from globals import OPTS, print_time - -from sram_base import sram_base -from bank import bank -from dff_buf_array import dff_buf_array -from dff_array import dff_array - -class sram_4bank(sram_base): - """ - Procedures specific to a four bank SRAM. - """ - def __init__(self, name, sram_config): - sram_base.__init__(self, name, sram_config) - - def compute_bank_offsets(self): - """ Compute the overall offsets for a four bank SRAM """ - - # The main difference is that the four bank SRAM has the data bus in the middle of the four banks - # as opposed to the top of the banks. - - # In 4 bank SRAM, the height is determined by the bank decoder and address flop - self.vertical_bus_height = 2*self.bank.height + 4*self.bank_to_bus_distance + self.data_bus_height \ - + self.supply_bus_height + self.msb_decoder.height + self.msb_address.width - # The address bus extends down through the power rails, but control and bank_sel bus don't - self.addr_bus_height = self.vertical_bus_height - - self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 0) - self.data_bus_offset = vector(0, self.bank.height + self.bank_to_bus_distance) - self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height \ - + self.bank.height + 2*self.bank_to_bus_distance) - self.control_bus_offset = vector(0, self.supply_bus_offset.y + self.supply_bus_height) - self.bank_sel_bus_offset = self.vertical_bus_offset + vector(self.m2_pitch*self.control_size,0) - self.addr_bus_offset = self.bank_sel_bus_offset.scale(1,0) + vector(self.m2_pitch*self.num_banks,0) - - # Control is placed at the top above the control bus and everything - self.control_logic_position = vector(0, self.control_bus_offset.y + self.control_bus_height + self.m1_pitch) - - # Bank select flops get put to the right of control logic above bank1 and the buses - # Leave a pitch to get the vdd rails up to M2 - self.msb_address_position = vector(self.bank_inst[1].lx() + 3*self.supply_rail_pitch, - self.supply_bus_offset.y + self.supply_bus_height \ - + 2*self.m1_pitch + self.msb_address.width) - - # Decoder goes above the MSB address flops, and is flipped in Y - # separate the two by a bank to bus distance for nwell rules, just in case - self.msb_decoder_position = self.msb_address_position + vector(self.msb_decoder.width, self.bank_to_bus_distance) - - - def add_modules(self): - """ Adds the modules and the buses to the top level """ - - self.compute_bus_sizes() - - self.add_banks() - - self.compute_bank_offsets() - - self.add_busses() - - self.add_logic() - - self.width = self.bank_inst[1].ur().x - self.height = max(self.control_logic_inst.uy(),self.msb_decoder_inst.uy()) - - def add_banks(self): - - # Placement of bank 0 (upper left) - bank_position_0 = vector(self.bank.width, - self.bank.height + self.data_bus_height + 2*self.bank_to_bus_distance) - self.bank_inst=[self.add_bank(0, bank_position_0, 1, -1)] - - # Placement of bank 1 (upper right) - x_off = self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance - bank_position_1 = vector(x_off, bank_position_0.y) - self.bank_inst.append(self.add_bank(1, bank_position_1, 1, 1)) - - # Placement of bank 2 (bottom left) - y_off = self.bank.height - bank_position_2 = vector(bank_position_0.x, y_off) - self.bank_inst.append(self.add_bank(2, bank_position_2, -1, -1)) - - # Placement of bank 3 (bottom right) - bank_position_3 = vector(bank_position_1.x, bank_position_2.y) - self.bank_inst.append(self.add_bank(3, bank_position_3, -1, 1)) - - - - def add_logic(self): - """ Add the control and MSB decode/bank select logic for four banks """ - - - self.add_control_logic(position=self.control_logic_position) - - self.msb_address_inst = self.add_inst(name="msb_address", - mod=self.msb_address, - offset=self.msb_address_position, - rotate=270) - - self.msb_bank_sel_addr = ["ADDR[{}]".format(i) for i in range(self.addr_size-2,self.addr_size,1)] - temp = list(self.msb_bank_sel_addr) - temp.extend(["msb{0}[{1}]".format(j,i) for i in range(2) for j in ["","_bar"]]) - temp.extend(["clk_buf", "vdd", "gnd"]) - self.connect_inst(temp) - - self.msb_decoder_inst = self.add_inst(name="msb_decoder", - mod=self.msb_decoder, - offset=self.msb_decoder_position, - mirror="MY") - temp = ["msb[{}]".format(i) for i in range(2)] - temp.extend(["bank_sel[{}]".format(i) for i in range(4)]) - temp.extend(["vdd", "gnd"]) - self.connect_inst(temp) - - def route_double_msb_address(self): - """ Route two MSB address bits and the bank decoder for 4-bank SRAM """ - - # connect the MSB flops to the address input bus - for i in [0,1]: - msb_pins = self.msb_address_inst.get_pins("din[{}]".format(i)) - for msb_pin in msb_pins: - if msb_pin.layer == "metal3": - msb_pin_pos = msb_pin.lc() - break - rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr[i]].x,msb_pin_pos.y) - self.add_path("metal3",[msb_pin_pos,rail_pos]) - self.add_via_center(("metal2","via2","metal3"),rail_pos) - - # Connect clk - clk_pin = self.msb_address_inst.get_pin("clk") - clk_pos = clk_pin.bc() - rail_pos = self.horz_control_bus_positions["clk_buf"] - bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y) - self.add_path("metal1",[clk_pos,bend_pos,rail_pos]) - - # Connect bank decoder outputs to the bank select vertical bus wires - for i in range(self.num_banks): - msb_pin = self.msb_decoder_inst.get_pin("out[{}]".format(i)) - msb_pin_pos = msb_pin.lc() - rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,msb_pin_pos.y) - self.add_path("metal1",[msb_pin_pos,rail_pos]) - self.add_via_center(("metal1","via1","metal2"),rail_pos) - - # connect MSB flop outputs to the bank decoder inputs - msb_pin = self.msb_address_inst.get_pin("dout[0]") - msb_pin_pos = msb_pin.rc() - in_pin = self.msb_decoder_inst.get_pin("in[0]") - in_pos = in_pin.bc() + vector(0,1*self.m2_pitch,) # pin is up from bottom - out_pos = msb_pin_pos + vector(1*self.m2_pitch,0) # route out to the right - up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer - self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos]) - self.add_via_center(("metal1","via1","metal2"),in_pos) - self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90) - - msb_pin = self.msb_address_inst.get_pin("dout[1]") - msb_pin_pos = msb_pin.rc() - in_pin = self.msb_decoder_inst.get_pin("in[1]") - in_pos = in_pin.bc() + vector(0,self.bitcell.height+self.m2_pitch) # route the next row up - out_pos = msb_pin_pos + vector(2*self.m2_pitch,0) # route out to the right - up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer - self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos]) - self.add_via_center(("metal1","via1","metal2"),in_pos) - self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90) - - self.route_double_msb_address_supplies() - - def route_double_msb_address_supplies(self): - """ Route the vdd/gnd bits of the 2-bit bank decoder. """ - - # Route the right-most vdd/gnd of the right upper bank to the top of the decoder - vdd_pins = self.bank_inst[1].get_pins("vdd") - left_bank_vdd_pin = None - right_bank_vdd_pin = None - for vdd_pin in vdd_pins: - if vdd_pin.layer != "metal2": - continue - if left_bank_vdd_pin == None or vdd_pin.lx()right_bank_vdd_pin.lx(): - right_bank_vdd_pin = vdd_pin - # Route to top - self.add_rect(layer="metal2", - offset=vdd_pin.ul(), - height=self.height-vdd_pin.uy(), - width=vdd_pin.width()) - - gnd_pins = self.bank_inst[1].get_pins("gnd") - left_bank_gnd_pin = None - right_bank_gnd_pin = None - for gnd_pin in gnd_pins: - if gnd_pin.layer != "metal2": - continue - if left_bank_gnd_pin == None or gnd_pin.lx()right_bank_gnd_pin.lx(): - right_bank_gnd_pin = gnd_pin - # Route to top - self.add_rect(layer="metal2", - offset=gnd_pin.ul(), - height=self.height-gnd_pin.uy(), - width=gnd_pin.width()) - - # Connect bank decoder vdd/gnd supplies using the previous bank pins - vdd_pins = self.msb_decoder_inst.get_pins("vdd") - for vdd_pin in vdd_pins: - if vdd_pin.layer != "metal1": - continue - rail1_pos = vector(left_bank_vdd_pin.cx(),vdd_pin.cy()) - rail2_pos = vector(right_bank_vdd_pin.cx(),vdd_pin.cy()) - self.add_path("metal1",[rail1_pos,rail2_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail1_pos, - rotate=90, - size=[1,3]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail2_pos, - rotate=90, - size=[1,3]) - gnd_pins = self.msb_decoder_inst.get_pins("gnd") - for gnd_pin in gnd_pins: - if gnd_pin.layer != "metal1": - continue - rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy()) - rail2_pos = vector(right_bank_gnd_pin.cx(),gnd_pin.cy()) - self.add_path("metal1",[rail1_pos,rail2_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail1_pos, - rotate=90, - size=[1,3]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail2_pos, - rotate=90, - size=[1,3]) - - # connect the bank MSB flop supplies - vdd_pins = self.msb_address_inst.get_pins("vdd") - # vdd pins go down to the rail - for vdd_pin in vdd_pins: - if vdd_pin.layer != "metal1": - continue - vdd_pos = vdd_pin.bc() - down_pos = vdd_pos - vector(0,self.m1_pitch) - rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y) - self.add_path("metal1",[vdd_pos,down_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=down_pos, - rotate=90) - self.add_path("metal2",[down_pos,rail_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail_pos) - # gnd pins go right to the rail - gnd_pins = self.msb_address_inst.get_pins("gnd") - for gnd_pin in gnd_pins: - if gnd_pin.layer != "metal2": - continue - rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy()) - self.add_path("metal1",[rail1_pos,gnd_pin.lc()]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=gnd_pin.lc(), - rotate=90) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail1_pos, - rotate=90, - size=[1,3]) - - - def route(self): - """ Route all of the signals for the four bank SRAM. """ - - self.route_shared_banks() - - # connect the data output to the data bus - for n in self.data_bus_names: - for i in [0,1]: - pin_pos = self.bank_inst[i].get_pin(n).bc() - rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y) - self.add_path("metal2",[pin_pos,rail_pos]) - self.add_via_center(("metal2","via2","metal3"),rail_pos) - - for i in [2,3]: - pin_pos = self.bank_inst[i].get_pin(n).uc() - rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y) - self.add_path("metal2",[pin_pos,rail_pos]) - self.add_via_center(("metal2","via2","metal3"),rail_pos) - - # route msb address bits - # route 2:4 decoder - self.route_double_msb_address() - - # connect the banks to the vertical address bus - # connect the banks to the vertical control bus - for n in self.addr_bus_names + self.control_bus_names: - # Skip these from the horizontal bus - if n in ["vdd", "gnd"]: continue - # This will be the bank select, so skip it - if n in self.msb_bank_sel_addr: continue - - for bank_id in [0,2]: - pin0_pos = self.bank_inst[bank_id].get_pin(n).rc() - pin1_pos = self.bank_inst[bank_id+1].get_pin(n).lc() - rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y) - self.add_path("metal3",[pin0_pos,pin1_pos]) - self.add_via_center(("metal2","via2","metal3"),rail_pos) - - - self.route_bank_supply_rails(left_banks=[0,2], bottom_banks=[2,3]) - - - def add_lvs_correspondence_points(self): - """ - This adds some points for easier debugging if LVS goes wrong. - These should probably be turned off by default though, since extraction - will show these as ports in the extracted netlist. - """ - - if self.num_banks==1: return - - for n in self.control_bus_names: - self.add_label(text=n, - layer="metal2", - offset=self.vert_control_bus_positions[n]) - for n in self.bank_sel_bus_names: - self.add_label(text=n, - layer="metal2", - offset=self.vert_control_bus_positions[n]) diff --git a/compiler/sram_base.py b/compiler/sram_base.py index 9a511bd4..b51ebae9 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -2,64 +2,67 @@ import sys import datetime import getpass import debug +from datetime import datetime from importlib import reload from vector import vector from globals import OPTS, print_time - +import logical_effort from design import design - -class sram_base(design): +from verilog import verilog +from lef import lef + +class sram_base(design, verilog, lef): """ Dynamically generated SRAM by connecting banks to control logic. The number of banks should be 1 , 2 or 4 """ def __init__(self, name, sram_config): design.__init__(self, name) + lef.__init__(self, ["metal1", "metal2", "metal3"]) + verilog.__init__(self) self.sram_config = sram_config sram_config.set_local_config(self) - self.total_write = OPTS.num_rw_ports + OPTS.num_w_ports - self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports - self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports - self.bank_insts = [] - + + #For logical effort delay calculations. + self.all_mods_except_control_done = False def add_pins(self): """ Add pins for entire SRAM. """ - self.read_index = [] - port_number = 0 - for port in range(OPTS.num_rw_ports): - self.read_index.append("{}".format(port_number)) - port_number += 1 - for port in range(OPTS.num_w_ports): - port_number += 1 - for port in range(OPTS.num_r_ports): - self.read_index.append("{}".format(port_number)) - port_number += 1 - - for port in range(self.total_write): + for port in self.write_ports: for bit in range(self.word_size): self.add_pin("DIN{0}[{1}]".format(port,bit),"INPUT") - for port in range(self.total_ports): + for port in self.all_ports: for bit in range(self.addr_size): self.add_pin("ADDR{0}[{1}]".format(port,bit),"INPUT") - # These are used to create the physical pins too - self.control_logic_inputs=self.control_logic.get_inputs() - self.control_logic_outputs=self.control_logic.get_outputs() + # These are used to create the physical pins + self.control_logic_inputs = [] + self.control_logic_outputs = [] + for port in self.all_ports: + if port in self.readwrite_ports: + self.control_logic_inputs.append(self.control_logic_rw.get_inputs()) + self.control_logic_outputs.append(self.control_logic_rw.get_outputs()) + elif port in self.write_ports: + self.control_logic_inputs.append(self.control_logic_w.get_inputs()) + self.control_logic_outputs.append(self.control_logic_w.get_outputs()) + else: + self.control_logic_inputs.append(self.control_logic_r.get_inputs()) + self.control_logic_outputs.append(self.control_logic_r.get_outputs()) - #self.add_pin_list(self.control_logic_inputs,"INPUT") - self.add_pin("csb","INPUT") - for port in range(self.total_write): + for port in self.all_ports: + self.add_pin("csb{}".format(port),"INPUT") + for port in self.readwrite_ports: self.add_pin("web{}".format(port),"INPUT") - self.add_pin("clk","INPUT") + for port in self.all_ports: + self.add_pin("clk{}".format(port),"INPUT") - for port in range(self.total_read): + for port in self.read_ports: for bit in range(self.word_size): - self.add_pin("DOUT{0}[{1}]".format(self.read_index[port],bit),"OUTPUT") + self.add_pin("DOUT{0}[{1}]".format(port,bit),"OUTPUT") self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") @@ -67,29 +70,66 @@ class sram_base(design): def create_netlist(self): """ Netlist creation """ - + + start_time = datetime.now() + # Must create the control logic before pins to get the pins self.add_modules() self.add_pins() + self.create_modules() # This is for the lib file if we don't create layout self.width=0 self.height=0 + + if not OPTS.is_unit_test: + print_time("Submodules",datetime.now(), start_time) + + def create_layout(self): """ Layout creation """ - self.place_modules() - self.route() + start_time = datetime.now() + self.place_instances() + if not OPTS.is_unit_test: + print_time("Placement",datetime.now(), start_time) + + start_time = datetime.now() + self.route_layout() + self.route_supplies() + if not OPTS.is_unit_test: + print_time("Routing",datetime.now(), start_time) + self.add_lvs_correspondence_points() self.offset_all_coordinates() - + highest_coord = self.find_highest_coords() self.width = highest_coord[0] self.height = highest_coord[1] - - self.DRC_LVS(final_verification=True) + start_time = datetime.now() + self.DRC_LVS(final_verification=True) + if not OPTS.is_unit_test: + print_time("Verification",datetime.now(), start_time) + + def create_modules(self): + debug.error("Must override pure virtual function.",-1) + + def route_supplies(self): + """ Route the supply grid and connect the pins to them. """ + + for inst in self.insts: + self.copy_power_pins(inst,"vdd") + self.copy_power_pins(inst,"gnd") + + from supply_router import supply_router as router + layer_stack =("metal3","via3","metal4") + rtr=router(layer_stack, self) + rtr.route() + + + def compute_bus_sizes(self): """ Compute the independent bus widths shared between two and four bank SRAMs """ @@ -116,95 +156,74 @@ class sram_base(design): "Bank is too small compared to control logic.") - def add_busses(self): """ Add the horizontal and vertical busses """ # Vertical bus # The order of the control signals on the control bus: - self.control_bus_names = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"] - self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2", - pitch=self.m2_pitch, - offset=self.vertical_bus_offset, - names=self.control_bus_names, - length=self.vertical_bus_height) + self.control_bus_names = [] + for port in self.all_ports: + self.control_bus_names[port] = ["clk_buf{}".format(port)] + wen = "w_en{}".format(port) + sen = "s_en{}".format(port) + pen = "p_en_bar{}".format(port) + if self.port_id[port] == "r": + self.control_bus_names[port].extend([sen, pen]) + elif self.port_id[port] == "w": + self.control_bus_names[port].extend([wen]) + else: + self.control_bus_names[port].extend([sen, wen, pen]) + self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2", + pitch=self.m2_pitch, + offset=self.vertical_bus_offset, + names=self.control_bus_names[port], + length=self.vertical_bus_height) - self.addr_bus_names=["A[{}]".format(i) for i in range(self.addr_size)] - self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2", - pitch=self.m2_pitch, - offset=self.addr_bus_offset, - names=self.addr_bus_names, - length=self.addr_bus_height)) + self.addr_bus_names=["A{0}[{1}]".format(port,i) for i in range(self.addr_size)] + self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2", + pitch=self.m2_pitch, + offset=self.addr_bus_offset, + names=self.addr_bus_names, + length=self.addr_bus_height)) - - self.bank_sel_bus_names = ["bank_sel[{}]".format(i) for i in range(self.num_banks)] - self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2", - pitch=self.m2_pitch, - offset=self.bank_sel_bus_offset, - names=self.bank_sel_bus_names, - length=self.vertical_bus_height)) - - - # Horizontal data bus - self.data_bus_names = ["DATA[{}]".format(i) for i in range(self.word_size)] - self.data_bus_positions = self.create_horizontal_pin_bus(layer="metal3", - pitch=self.m3_pitch, - offset=self.data_bus_offset, - names=self.data_bus_names, - length=self.data_bus_width) - - # Horizontal control logic bus - # vdd/gnd in bus go along whole SRAM - # FIXME: Fatten these wires? - self.horz_control_bus_positions = self.create_horizontal_bus(layer="metal1", - pitch=self.m1_pitch, - offset=self.supply_bus_offset, - names=["vdd"], - length=self.supply_bus_width) - # The gnd rail must not be the entire width since we protrude the right-most vdd rail up for - # the decoder in 4-bank SRAMs - self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1", - pitch=self.m1_pitch, - offset=self.supply_bus_offset+vector(0,self.m1_pitch), - names=["gnd"], - length=self.supply_bus_width)) - self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1", - pitch=self.m1_pitch, - offset=self.control_bus_offset, - names=self.control_bus_names, - length=self.control_bus_width)) - - - - - - def route_vdd_gnd(self): - """ Propagate all vdd/gnd pins up to this level for all modules """ - - # These are the instances that every bank has - top_instances = [self.bitcell_array_inst, - self.precharge_array_inst, - self.sense_amp_array_inst, - self.write_driver_array_inst, - self.tri_gate_array_inst, - self.row_decoder_inst, - self.wordline_driver_inst] - # Add these if we use the part... - if self.col_addr_size > 0: - top_instances.append(self.col_decoder_inst) - top_instances.append(self.col_mux_array_inst) - if self.num_banks > 1: - top_instances.append(self.bank_select_inst) + self.bank_sel_bus_names = ["bank_sel{0}_{1}".format(port,i) for i in range(self.num_banks)] + self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2", + pitch=self.m2_pitch, + offset=self.bank_sel_bus_offset, + names=self.bank_sel_bus_names, + length=self.vertical_bus_height)) + + # Horizontal data bus + self.data_bus_names = ["DATA{0}[{1}]".format(port,i) for i in range(self.word_size)] + self.data_bus_positions = self.create_horizontal_pin_bus(layer="metal3", + pitch=self.m3_pitch, + offset=self.data_bus_offset, + names=self.data_bus_names, + length=self.data_bus_width) + + # Horizontal control logic bus + # vdd/gnd in bus go along whole SRAM + # FIXME: Fatten these wires? + self.horz_control_bus_positions = self.create_horizontal_bus(layer="metal1", + pitch=self.m1_pitch, + offset=self.supply_bus_offset, + names=["vdd"], + length=self.supply_bus_width) + # The gnd rail must not be the entire width since we protrude the right-most vdd rail up for + # the decoder in 4-bank SRAMs + self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1", + pitch=self.m1_pitch, + offset=self.supply_bus_offset+vector(0,self.m1_pitch), + names=["gnd"], + length=self.supply_bus_width)) + self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1", + pitch=self.m1_pitch, + offset=self.control_bus_offset, + names=self.control_bus_names[port], + length=self.control_bus_width)) - for inst in top_instances: - # Column mux has no vdd - if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst): - self.copy_layout_pin(inst, "vdd") - # Precharge has no gnd - if inst != self.precharge_array_inst: - self.copy_layout_pin(inst, "gnd") - + def add_multi_bank_modules(self): """ Create the multibank address flops and bank decoder """ @@ -218,33 +237,25 @@ class sram_base(design): self.msb_decoder = self.bank.decoder.pre2_4 self.add_mod(self.msb_decoder) + def add_modules(self): """ Create all the modules that will be used """ c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell = self.mod_bitcell() - - c = reload(__import__(OPTS.control_logic)) - self.mod_control_logic = getattr(c, OPTS.control_logic) - - - from control_logic import control_logic - # Create the control logic module - self.control_logic = self.mod_control_logic(num_rows=self.num_rows) - self.add_mod(self.control_logic) # Create the address and control flops (but not the clk) from dff_array import dff_array - self.row_addr_dff = dff_array(name="row_addr_dff", rows=self.row_addr_size*self.total_ports, columns=1) + self.row_addr_dff = dff_array(name="row_addr_dff", rows=self.row_addr_size, columns=1) self.add_mod(self.row_addr_dff) if self.col_addr_size > 0: - self.col_addr_dff = dff_array(name="col_addr_dff", rows=1, columns=self.col_addr_size*self.total_ports) + self.col_addr_dff = dff_array(name="col_addr_dff", rows=1, columns=self.col_addr_size) self.add_mod(self.col_addr_dff) else: self.col_addr_dff = None - self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size*self.total_write) + self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size) self.add_mod(self.data_dff) # Create the bank module (up to four are instantiated) @@ -261,8 +272,32 @@ class sram_base(design): self.supply_rail_width = self.bank.supply_rail_width self.supply_rail_pitch = self.bank.supply_rail_pitch + + #The control logic can resize itself based on the other modules. Requires all other modules added before control logic. + self.all_mods_except_control_done = True - + c = reload(__import__(OPTS.control_logic)) + self.mod_control_logic = getattr(c, OPTS.control_logic) + + # Create the control logic module for each port type + if len(self.readwrite_ports)>0: + self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows, + words_per_row=self.words_per_row, + sram=self, + port_type="rw") + self.add_mod(self.control_logic_rw) + if len(self.writeonly_ports)>0: + self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows, + words_per_row=self.words_per_row, + sram=self, + port_type="w") + self.add_mod(self.control_logic_w) + if len(self.readonly_ports)>0: + self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows, + words_per_row=self.words_per_row, + sram=self, + port_type="r") + self.add_mod(self.control_logic_r) def create_bank(self,bank_num): """ Create a bank """ @@ -270,23 +305,27 @@ class sram_base(design): mod=self.bank)) temp = [] - for port in range(self.total_read): + for port in self.read_ports: for bit in range(self.word_size): - temp.append("DOUT{0}[{1}]".format(self.read_index[port],bit)) - for port in range(self.total_write): + temp.append("DOUT{0}[{1}]".format(port,bit)) + for port in self.write_ports: for bit in range(self.word_size): temp.append("BANK_DIN{0}[{1}]".format(port,bit)) - for port in range(self.total_ports): + for port in self.all_ports: for bit in range(self.bank_addr_size): temp.append("A{0}[{1}]".format(port,bit)) if(self.num_banks > 1): - for port in range(self.total_ports): + for port in self.all_ports: temp.append("bank_sel{0}[{1}]".format(port,bank_num)) - for port in range(self.total_read): - temp.append("s_en{0}".format(self.read_index[port])) - for port in range(self.total_write): + for port in self.read_ports: + temp.append("s_en{0}".format(port)) + for port in self.read_ports: + temp.append("p_en_bar{0}".format(port)) + for port in self.write_ports: temp.append("w_en{0}".format(port)) - temp.extend(["clk_buf_bar","clk_buf" , "vdd", "gnd"]) + for port in self.all_ports: + temp.append("wl_en{0}".format(port)) + temp.extend(["vdd", "gnd"]) self.connect_inst(temp) return self.bank_insts[-1] @@ -325,68 +364,96 @@ class sram_base(design): def create_row_addr_dff(self): """ Add all address flops for the main decoder """ - inst = self.add_inst(name="row_address", - mod=self.row_addr_dff) - - # inputs, outputs/output/bar - inputs = [] - outputs = [] - for port in range(self.total_ports): - for i in range(self.row_addr_size): - inputs.append("ADDR{}[{}]".format(port,i+self.col_addr_size)) - outputs.append("A{}[{}]".format(port,i+self.col_addr_size)) + insts = [] + for port in self.all_ports: + insts.append(self.add_inst(name="row_address{}".format(port), + mod=self.row_addr_dff)) + + # inputs, outputs/output/bar + inputs = [] + 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)) + + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + + return insts - self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) - return inst def create_col_addr_dff(self): """ Add and place all address flops for the column decoder """ - inst = self.add_inst(name="col_address", - mod=self.col_addr_dff) - - # inputs, outputs/output/bar - inputs = [] - outputs = [] - for port in range(self.total_ports): - for i in range(self.col_addr_size): - inputs.append("ADDR{}[{}]".format(port,i)) - outputs.append("A{}[{}]".format(port,i)) + insts = [] + for port in self.all_ports: + insts.append(self.add_inst(name="col_address{}".format(port), + mod=self.col_addr_dff)) + + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.col_addr_size): + inputs.append("ADDR{}[{}]".format(port,bit)) + outputs.append("A{}[{}]".format(port,bit)) - self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) - return inst - + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + + return insts + + def create_data_dff(self): """ Add and place all data flops """ - inst = self.add_inst(name="data_dff", - mod=self.data_dff) - - # inputs, outputs/output/bar - inputs = [] - outputs = [] - for port in range(self.total_write): - for i in range(self.word_size): - inputs.append("DIN{}[{}]".format(port,i)) - outputs.append("BANK_DIN{}[{}]".format(port,i)) + insts = [] + for port in self.all_ports: + if port in self.write_ports: + insts.append(self.add_inst(name="data_dff{}".format(port), + mod=self.data_dff)) + else: + insts.append(None) + continue + + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.word_size): + inputs.append("DIN{}[{}]".format(port,bit)) + outputs.append("BANK_DIN{}[{}]".format(port,bit)) - self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) - return inst - - def create_control_logic(self, port): - """ Add and place control logic """ - inst = self.add_inst(name="control", - mod=self.control_logic) - - self.connect_inst(["csb", "web{}".format(port), "clk", - "s_en{}".format(port), "w_en{}".format(port), "clk_buf_bar", "clk_buf", - "vdd", "gnd"]) - - #self.connect_inst(self.control_logic_inputs + self.control_logic_outputs + ["vdd", "gnd"]) - return inst - + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + return insts + + def create_control_logic(self): + """ Add control logic instances """ + insts = [] + for port in self.all_ports: + if port in self.readwrite_ports: + mod = self.control_logic_rw + elif port in self.write_ports: + mod = self.control_logic_w + else: + mod = self.control_logic_r + + insts.append(self.add_inst(name="control{}".format(port), mod=mod)) + # Inputs + temp = ["csb{}".format(port)] + if port in self.readwrite_ports: + temp.append("web{}".format(port)) + temp.append("clk{}".format(port)) + + # Ouputs + if port in self.read_ports: + temp.append("s_en{}".format(port)) + if port in self.write_ports: + temp.append("w_en{}".format(port)) + if port in self.read_ports: + temp.append("p_en_bar{}".format(port)) + temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"]) + self.connect_inst(temp) + + return insts def connect_rail_from_left_m2m3(self, src_pin, dest_pin): @@ -397,14 +464,14 @@ class sram_base(design): self.add_via_center(layers=("metal2","via2","metal3"), offset=src_pin.rc(), rotate=90) - + + def connect_rail_from_left_m2m1(self, src_pin, dest_pin): """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ in_pos = src_pin.rc() out_pos = vector(dest_pin.cx(), in_pos.y) self.add_wire(("metal2","via1","metal1"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)]) - def sp_write(self, sp_name): # Write the entire spice of the object to the file @@ -430,8 +497,42 @@ class sram_base(design): del usedMODS sp.close() - def analytical_delay(self,slew,load): - """ LH and HL are the same in analytical model. """ - return self.bank.analytical_delay(slew,load) - + def analytical_delay(self, vdd, slew,load): + """ LH and HL are the same in analytical model. """ + return self.bank.analytical_delay(vdd,slew,load) + + def determine_wordline_stage_efforts(self, inp_is_rise=True): + """Get the all the stage efforts for each stage in the path from clk_buf to a wordline""" + stage_effort_list = [] + + #Clk_buf originates from the control logic so only the bank is related to the wordline path + external_wordline_cout = 0 #No loading on the wordline other than in the bank. + stage_effort_list += self.bank.determine_wordline_stage_efforts(external_wordline_cout, inp_is_rise) + + return stage_effort_list + + def get_wl_en_cin(self): + """Gets the capacitive load the of clock (clk_buf) for the sram""" + #As clk_buf is an output of the control logic. The cap for that module is not determined here. + #Only the wordline drivers within the bank use this signal + bank_clk_cin = self.bank.get_wl_en_cin() + + return bank_clk_cin + + def get_clk_bar_cin(self): + """Gets the capacitive load the of clock (clk_buf_bar) for the sram""" + #As clk_buf_bar is an output of the control logic. The cap for that module is not determined here. + #Only the precharge cells use this signal (other than the control logic) + bank_clk_cin = self.bank.get_clk_bar_cin() + return bank_clk_cin + + def get_sen_cin(self): + """Gets the capacitive load the of sense amp enable for the sram""" + #As clk_buf_bar is an output of the control logic. The cap for that module is not determined here. + #Only the sense_amps use this signal (other than the control logic) + bank_sen_cin = self.bank.get_sen_cin() + return bank_sen_cin + + + diff --git a/compiler/sram_config.py b/compiler/sram_config.py index e7c80fd8..e4f94b4a 100644 --- a/compiler/sram_config.py +++ b/compiler/sram_config.py @@ -14,7 +14,7 @@ class sram_config: # This will get over-written when we determine the organization self.words_per_row = None - # Move the module names to this? + self.compute_sizes() def set_local_config(self, module): @@ -53,6 +53,20 @@ class sram_config: # Estimate the number of rows given the tentative words per row self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size) self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row) + + debug.info(1,"Words per row: {}".format(self.words_per_row)) + self.recompute_sizes() + + def recompute_sizes(self): + """ + Calculate the auxiliary values assuming fixed number of words per row. + This can be called multiple times from the unit test when we reconfigure an + SRAM for testing. + """ + + # If the banks changed + self.num_words_per_bank = self.num_words/self.num_banks + self.num_bits_per_bank = self.word_size*self.num_words_per_bank # Fix the number of columns and rows self.num_cols = int(self.words_per_row*self.word_size) @@ -64,7 +78,6 @@ class sram_config: self.bank_addr_size = self.col_addr_size + self.row_addr_size self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) - debug.info(1,"Words per row: {}".format(self.words_per_row)) def estimate_words_per_row(self,tentative_num_cols, word_size): """ @@ -74,10 +87,14 @@ class sram_config: if tentative_num_cols < 1.5*word_size: return 1 - elif tentative_num_cols > 3*word_size: + elif tentative_num_cols < 3*word_size: + return 2 + elif tentative_num_cols < 6*word_size: return 4 else: - return 2 + if tentative_num_cols > 16*word_size: + debug.warning("Reaching column mux size limit. Consider increasing above 8-way.") + return 8 def amend_words_per_row(self,tentative_num_rows, words_per_row): """ diff --git a/compiler/tests/00_code_format_check_test.py b/compiler/tests/00_code_format_check_test.py index 7ed46e50..869e81bd 100755 --- a/compiler/tests/00_code_format_check_test.py +++ b/compiler/tests/00_code_format_check_test.py @@ -31,12 +31,12 @@ class code_format_test(openram_test): continue if re.search("testutils.py$", code): continue - if re.search("grid.py$", code): - continue if re.search("globals.py$", code): continue if re.search("openram.py$", code): continue + if re.search("sram.py$", code): + continue if re.search("gen_stimulus.py$", code): continue errors += check_print_output(code) @@ -52,7 +52,7 @@ def setup_files(path): for f in current_files: files.append(os.path.join(dir, f)) nametest = re.compile("\.py$", re.IGNORECASE) - select_files = filter(nametest.search, files) + select_files = list(filter(nametest.search, files)) return select_files @@ -102,20 +102,21 @@ def check_print_output(file_name): """Check if any files (except debug.py) call the _print_ function. We should use the debug output with verbosity instead!""" file = open(file_name, "r+b") - line = file.read() + line = file.read().decode('utf-8') # skip comments with a hash line = re.sub(r'#.*', '', line) # skip doc string comments line=re.sub(r'\"\"\"[^\"]*\"\"\"', '', line, flags=re.S|re.M) - count = len(re.findall("\s*print\s+", line)) + count = len(re.findall("[^p]+print\(", line)) if count > 0: debug.info(0, "\nFound " + str(count) + " _print_ calls " + str(file_name)) + file.close() return(count) -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/01_library_drc_test.py b/compiler/tests/01_library_drc_test.py index b809c14d..046e6378 100755 --- a/compiler/tests/01_library_drc_test.py +++ b/compiler/tests/01_library_drc_test.py @@ -39,7 +39,7 @@ def setup_files(): return (gds_dir, gds_files) -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py index 0b35f159..4ec40dc7 100755 --- a/compiler/tests/02_library_lvs_test.py +++ b/compiler/tests/02_library_lvs_test.py @@ -16,22 +16,25 @@ class library_lvs_test(openram_test): import verify (gds_dir, sp_dir, allnames) = setup_files() + drc_errors = 0 lvs_errors = 0 debug.info(1, "Performing LVS on: " + ", ".join(allnames)) for f in allnames: gds_name = "{0}/{1}.gds".format(gds_dir, f) sp_name = "{0}/{1}.sp".format(sp_dir, f) + name = re.sub('\.gds$', '', f) if not os.path.isfile(gds_name): lvs_errors += 1 debug.error("Missing GDS file {}".format(gds_name)) if not os.path.isfile(sp_name): lvs_errors += 1 debug.error("Missing SPICE file {}".format(gds_name)) + drc_errors += verify.run_drc(name, gds_name) lvs_errors += verify.run_lvs(f, gds_name, sp_name) # fail if the error count is not zero - self.assertEqual(lvs_errors, 0) + self.assertEqual(drc_errors+lvs_errors, 0) globals.end_openram() def setup_files(): @@ -59,7 +62,7 @@ def setup_files(): return (gds_dir, sp_dir, allnames) -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py index 2fab1c4e..33aa45ae 100755 --- a/compiler/tests/03_contact_test.py +++ b/compiler/tests/03_contact_test.py @@ -43,7 +43,7 @@ class contact_test(openram_test): -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_path_test.py b/compiler/tests/03_path_test.py index f70d00be..915c5c78 100755 --- a/compiler/tests/03_path_test.py +++ b/compiler/tests/03_path_test.py @@ -84,7 +84,7 @@ class path_test(openram_test): -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_ptx_1finger_nmos_test.py b/compiler/tests/03_ptx_1finger_nmos_test.py index 727c24f0..9a81810e 100755 --- a/compiler/tests/03_ptx_1finger_nmos_test.py +++ b/compiler/tests/03_ptx_1finger_nmos_test.py @@ -25,7 +25,7 @@ class ptx_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_ptx_1finger_pmos_test.py b/compiler/tests/03_ptx_1finger_pmos_test.py index 04b9ab64..a3ed99ff 100755 --- a/compiler/tests/03_ptx_1finger_pmos_test.py +++ b/compiler/tests/03_ptx_1finger_pmos_test.py @@ -25,7 +25,7 @@ class ptx_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_ptx_3finger_nmos_test.py b/compiler/tests/03_ptx_3finger_nmos_test.py index 20343b2e..e1febdbc 100755 --- a/compiler/tests/03_ptx_3finger_nmos_test.py +++ b/compiler/tests/03_ptx_3finger_nmos_test.py @@ -27,7 +27,7 @@ class ptx_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_ptx_3finger_pmos_test.py b/compiler/tests/03_ptx_3finger_pmos_test.py index 37933702..af9a5d42 100755 --- a/compiler/tests/03_ptx_3finger_pmos_test.py +++ b/compiler/tests/03_ptx_3finger_pmos_test.py @@ -27,7 +27,7 @@ class ptx_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_ptx_4finger_nmos_test.py b/compiler/tests/03_ptx_4finger_nmos_test.py index 09788a5e..08a20898 100755 --- a/compiler/tests/03_ptx_4finger_nmos_test.py +++ b/compiler/tests/03_ptx_4finger_nmos_test.py @@ -27,7 +27,7 @@ class ptx_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_ptx_4finger_pmos_test.py b/compiler/tests/03_ptx_4finger_pmos_test.py index f43d7dc7..01857eda 100755 --- a/compiler/tests/03_ptx_4finger_pmos_test.py +++ b/compiler/tests/03_ptx_4finger_pmos_test.py @@ -27,7 +27,7 @@ class ptx_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_wire_test.py b/compiler/tests/03_wire_test.py index 557fee5b..1b18e14b 100755 --- a/compiler/tests/03_wire_test.py +++ b/compiler/tests/03_wire_test.py @@ -121,7 +121,7 @@ class wire_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/11_dff_inv_test.py b/compiler/tests/04_pand2_test.py similarity index 72% rename from compiler/tests/11_dff_inv_test.py rename to compiler/tests/04_pand2_test.py index 43d49246..68433e96 100755 --- a/compiler/tests/11_dff_inv_test.py +++ b/compiler/tests/04_pand2_test.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Run a regression test on a dff_inv. +Run a regression test on a pand2 cell """ import unittest @@ -11,14 +11,17 @@ import globals from globals import OPTS import debug -class dff_inv_test(openram_test): +class pand2_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import dff_inv + global verify + import verify - debug.info(2, "Testing dff_inv 4x") - a = dff_inv.dff_inv(4) + import pand2 + + debug.info(2, "Testing pand2 gate 4x") + a = pand2.pand2(4) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 0b6bd8f5..e5dbbc5e 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -94,7 +94,7 @@ class pbitcell_test(openram_test): -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/11_dff_inv_array_test.py b/compiler/tests/04_pbuf_test.py similarity index 52% rename from compiler/tests/11_dff_inv_array_test.py rename to compiler/tests/04_pbuf_test.py index 2196a3f2..f784c671 100755 --- a/compiler/tests/11_dff_inv_array_test.py +++ b/compiler/tests/04_pbuf_test.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Run a regression test on a dff_array. +Run a regression test on a 2-row buffer cell """ import unittest @@ -11,22 +11,17 @@ import globals from globals import OPTS import debug -class dff_inv_array_test(openram_test): +class pbuf_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import dff_inv_array + global verify + import verify - debug.info(2, "Testing dff_inv_array for 3x3") - a = dff_inv_array.dff_inv_array(rows=3, columns=3) - self.local_check(a) + import pbuf - debug.info(2, "Testing dff_inv_array for 1x3") - a = dff_inv_array.dff_inv_array(rows=1, columns=3) - self.local_check(a) - - debug.info(2, "Testing dff_inv_array for 3x1") - a = dff_inv_array.dff_inv_array(rows=3, columns=1) + debug.info(2, "Testing inverter/buffer 4x 8x") + a = pbuf.pbuf(8) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/04_pdriver_test.py b/compiler/tests/04_pdriver_test.py new file mode 100755 index 00000000..8db39d8c --- /dev/null +++ b/compiler/tests/04_pdriver_test.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 2-row buffer cell +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class pdriver_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + global verify + import verify + + import pdriver + + debug.info(2, "Testing inverter/buffer 4x 8x") + # a tests the error message for specifying conflicting conditions + #a = pdriver.pdriver(fanout_size = 4,size_list = [1,2,4,8]) + b = pdriver.pdriver(size_list = [1,2,4,8]) + c = pdriver.pdriver(fanout_size = 50) + d = pdriver.pdriver(fanout_size = 50, neg_polarity = True) + e = pdriver.pdriver(fanout_size = 64) + f = pdriver.pdriver(fanout_size = 64, neg_polarity = True) + #self.local_check(a) + self.local_check(b) + self.local_check(c) + self.local_check(d) + self.local_check(e) + self.local_check(f) + + globals.end_openram() + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/04_pinv_10x_test.py b/compiler/tests/04_pinv_10x_test.py index 3a7f846a..d457d2a9 100755 --- a/compiler/tests/04_pinv_10x_test.py +++ b/compiler/tests/04_pinv_10x_test.py @@ -25,7 +25,7 @@ class pinv_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pinv_1x_beta_test.py b/compiler/tests/04_pinv_1x_beta_test.py index c1bb6aba..77ff5454 100755 --- a/compiler/tests/04_pinv_1x_beta_test.py +++ b/compiler/tests/04_pinv_1x_beta_test.py @@ -24,7 +24,7 @@ class pinv_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pinv_1x_test.py b/compiler/tests/04_pinv_1x_test.py index 555aa0e5..49cb1cb1 100755 --- a/compiler/tests/04_pinv_1x_test.py +++ b/compiler/tests/04_pinv_1x_test.py @@ -23,7 +23,7 @@ class pinv_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pinv_2x_test.py b/compiler/tests/04_pinv_2x_test.py index 6882a719..84bc55ee 100755 --- a/compiler/tests/04_pinv_2x_test.py +++ b/compiler/tests/04_pinv_2x_test.py @@ -25,7 +25,7 @@ class pinv_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pinvbuf_test.py b/compiler/tests/04_pinvbuf_test.py index 9c55ebe3..d35f1ec7 100755 --- a/compiler/tests/04_pinvbuf_test.py +++ b/compiler/tests/04_pinvbuf_test.py @@ -23,7 +23,7 @@ class pinvbuf_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pnand2_test.py b/compiler/tests/04_pnand2_test.py index b6739e4e..a2ac9288 100755 --- a/compiler/tests/04_pnand2_test.py +++ b/compiler/tests/04_pnand2_test.py @@ -27,7 +27,7 @@ class pnand2_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pnand3_test.py b/compiler/tests/04_pnand3_test.py index db3817f5..f6daedda 100755 --- a/compiler/tests/04_pnand3_test.py +++ b/compiler/tests/04_pnand3_test.py @@ -27,7 +27,7 @@ class pnand3_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pnor2_test.py b/compiler/tests/04_pnor2_test.py index 978c03ad..ce4b19ae 100755 --- a/compiler/tests/04_pnor2_test.py +++ b/compiler/tests/04_pnor2_test.py @@ -26,7 +26,7 @@ class pnor2_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py index a68585bb..6c0cfe56 100755 --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -18,28 +18,32 @@ class precharge_test(openram_test): import precharge import tech + # check precharge in single port debug.info(2, "Checking precharge for handmade bitcell") tx = precharge.precharge(name="precharge_driver", size=1) self.local_check(tx) - if OPTS.multiport_check: - debug.info(2, "Checking precharge for pbitcell") - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 - tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(tx) - - tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl1", bitcell_br="br1") - self.local_check(tx) - - tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl2", bitcell_br="br2") - self.local_check(tx) + # check precharge in multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + debug.info(2, "Checking precharge for pbitcell (innermost connections)") + tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(tx) + + debug.info(2, "Checking precharge for pbitcell (innermost connections)") + tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl1", bitcell_br="br1") + self.local_check(tx) + + debug.info(2, "Checking precharge for pbitcell (outermost connections)") + tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl2", bitcell_br="br2") + self.local_check(tx) globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_replica_pbitcell_test.py b/compiler/tests/04_replica_pbitcell_test.py new file mode 100755 index 00000000..ce9f00b9 --- /dev/null +++ b/compiler/tests/04_replica_pbitcell_test.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a replica pbitcell +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class replica_pbitcell_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + import replica_pbitcell + import tech + + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + + debug.info(2, "Checking replica bitcell using pbitcell (small cell)") + tx = replica_pbitcell.replica_pbitcell() + self.local_check(tx) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + debug.info(2, "Checking replica bitcell using pbitcell (large cell)") + tx = replica_pbitcell.replica_pbitcell() + self.local_check(tx) + + 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() diff --git a/compiler/tests/04_single_level_column_mux_test.py b/compiler/tests/04_single_level_column_mux_test.py index 6437c58d..c43b15fd 100755 --- a/compiler/tests/04_single_level_column_mux_test.py +++ b/compiler/tests/04_single_level_column_mux_test.py @@ -19,29 +19,29 @@ class single_level_column_mux_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) import single_level_column_mux import tech - + + # check single level column mux in single port debug.info(2, "Checking column mux") tx = single_level_column_mux.single_level_column_mux(tx_size=8) self.local_check(tx) - if OPTS.multiport_check: - debug.info(2, "Checking column mux for pbitcell") - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 - tx = single_level_column_mux.single_level_column_mux(tx_size=8, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(tx) - - tx = single_level_column_mux.single_level_column_mux(tx_size=8, bitcell_bl="bl1", bitcell_br="br1") - self.local_check(tx) - - tx = single_level_column_mux.single_level_column_mux(tx_size=8, bitcell_bl="bl2", bitcell_br="br2") - self.local_check(tx) + # check single level column mux in multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + debug.info(2, "Checking column mux for pbitcell (innermost connections)") + tx = single_level_column_mux.single_level_column_mux(tx_size=8, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(tx) + + debug.info(2, "Checking column mux for pbitcell (outermost connections)") + tx = single_level_column_mux.single_level_column_mux(tx_size=8, bitcell_bl="bl2", bitcell_br="br2") + self.local_check(tx) globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/05_bitcell_1rw_1r_array_test.py b/compiler/tests/05_bitcell_1rw_1r_array_test.py new file mode 100755 index 00000000..1223085e --- /dev/null +++ b/compiler/tests/05_bitcell_1rw_1r_array_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a basic array +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 05_bitcell_1rw_1r_array_test") + +class bitcell_1rw_1r_array_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + import bitcell_array + + debug.info(2, "Testing 4x4 array for cell_1rw_1r") + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + a = bitcell_array.bitcell_array(name="bitcell_1rw_1r_array", cols=4, rows=4) + self.local_check(a) + + 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() diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py index 4ea5c65a..93668e05 100755 --- a/compiler/tests/05_bitcell_array_test.py +++ b/compiler/tests/05_bitcell_array_test.py @@ -25,7 +25,7 @@ class array_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index 4fc75ac5..4da5bec9 100755 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -44,7 +44,7 @@ class pbitcell_array_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index ab4f5f90..09201149 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -28,6 +28,7 @@ class hierarchical_decoder_test(openram_test): # a = hierarchical_decoder.hierarchical_decoder(rows=8) # self.local_check(a) + # check hierarchical decoder for single port debug.info(1, "Testing 16 row sample for hierarchical_decoder") a = hierarchical_decoder.hierarchical_decoder(rows=16) self.local_check(a) @@ -43,10 +44,32 @@ class hierarchical_decoder_test(openram_test): debug.info(1, "Testing 512 row sample for hierarchical_decoder") a = hierarchical_decoder.hierarchical_decoder(rows=512) self.local_check(a) + + # check hierarchical decoder for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") + a = hierarchical_decoder.hierarchical_decoder(rows=16) + self.local_check(a) + + debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)") + a = hierarchical_decoder.hierarchical_decoder(rows=32) + self.local_check(a) + + debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)") + a = hierarchical_decoder.hierarchical_decoder(rows=128) + self.local_check(a) + + debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)") + a = hierarchical_decoder.hierarchical_decoder(rows=512) + self.local_check(a) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py index 80b95b46..e16916d6 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -18,13 +18,24 @@ class hierarchical_predecode2x4_test(openram_test): import hierarchical_predecode2x4 as pre import tech + # checking hierarchical precode 2x4 for single port debug.info(1, "Testing sample for hierarchy_predecode2x4") a = pre.hierarchical_predecode2x4() self.local_check(a) + # checking hierarchical precode 2x4 for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(1, "Testing sample for hierarchy_predecode2x4 (multi-port case)") + a = pre.hierarchical_predecode2x4() + self.local_check(a) + globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py index 0974ced3..ed5da57c 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -18,13 +18,24 @@ class hierarchical_predecode3x8_test(openram_test): import hierarchical_predecode3x8 as pre import tech + # checking hierarchical precode 3x8 for single port debug.info(1, "Testing sample for hierarchy_predecode3x8") a = pre.hierarchical_predecode3x8() self.local_check(a) + + # checking hierarchical precode 3x8 for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(1, "Testing sample for hierarchy_predecode3x8 (multi-port case)") + a = pre.hierarchical_predecode3x8() + self.local_check(a) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py index 77a1358e..800292b6 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_single_level_column_mux_array_test.py @@ -16,6 +16,7 @@ class single_level_column_mux_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) import single_level_column_mux_array + # check single level column mux array in single port debug.info(1, "Testing sample for 2-way column_mux_array") a = single_level_column_mux_array.single_level_column_mux_array(columns=16, word_size=8) self.local_check(a) @@ -28,37 +29,32 @@ class single_level_column_mux_test(openram_test): a = single_level_column_mux_array.single_level_column_mux_array(columns=32, word_size=4) self.local_check(a) - if OPTS.multiport_check: - debug.info(2, "Checking column mux array for pbitcell") - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 - - debug.info(1, "Testing sample for 2-way column_mux_array") - a = single_level_column_mux_array.single_level_column_mux_array(columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(a) + # check single level column mux array in multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + debug.info(1, "Testing sample for 2-way column_mux_array in multi-port") + a = single_level_column_mux_array.single_level_column_mux_array(columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(a) - debug.info(1, "Testing sample for 4-way column_mux_array") - a = single_level_column_mux_array.single_level_column_mux_array(columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(a) + debug.info(1, "Testing sample for 4-way column_mux_array in multi-port") + a = single_level_column_mux_array.single_level_column_mux_array(columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(a) - debug.info(1, "Testing sample for 8-way column_mux_array") - a = single_level_column_mux_array.single_level_column_mux_array(columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(a) - - debug.info(1, "Testing sample for 8-way column_mux_array") - a = single_level_column_mux_array.single_level_column_mux_array(columns=32, word_size=4, bitcell_bl="bl1", bitcell_br="br1") - self.local_check(a) - - debug.info(1, "Testing sample for 8-way column_mux_array") - a = single_level_column_mux_array.single_level_column_mux_array(columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2") - self.local_check(a) + debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (innermost connections)") + a = single_level_column_mux_array.single_level_column_mux_array(columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(a) + + debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)") + a = single_level_column_mux_array.single_level_column_mux_array(columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2") + self.local_check(a) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index 1e82c69b..cdf6100e 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -18,29 +18,32 @@ class precharge_test(openram_test): import precharge_array import tech + # check precharge array in single port debug.info(2, "Checking 3 column precharge") pc = precharge_array.precharge_array(columns=3) self.local_check(pc) - if OPTS.multiport_check: - debug.info(2, "Checking precharge array for pbitcell") - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 - - pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(pc) - - pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl1", bitcell_br="br1") - self.local_check(pc) - - pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl2", bitcell_br="br2") - self.local_check(pc) + # check precharge array in multi-port + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell") + pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(pc) + + # debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") + # pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl0", bitcell_br="br0") + # self.local_check(pc) + + # debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)") + # pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl2", bitcell_br="br2") + # self.local_check(pc) globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/08_wordline_driver_test.py b/compiler/tests/08_wordline_driver_test.py index f69a3c02..369b6774 100755 --- a/compiler/tests/08_wordline_driver_test.py +++ b/compiler/tests/08_wordline_driver_test.py @@ -20,23 +20,24 @@ class wordline_driver_test(openram_test): import wordline_driver import tech + # check wordline driver for single port debug.info(2, "Checking driver") tx = wordline_driver.wordline_driver(rows=8) self.local_check(tx) - if OPTS.multiport_check: - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - debug.info(2, "Checking driver (multi-port case)") - tx = wordline_driver.wordline_driver(rows=8) - self.local_check(tx) + # check wordline driver for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(2, "Checking driver (multi-port case)") + tx = wordline_driver.wordline_driver(rows=8) + self.local_check(tx) globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py index d167a752..a18631f9 100755 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -17,6 +17,7 @@ class sense_amp_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) import sense_amp_array + # check sense amp array for single port debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=2) self.local_check(a) @@ -25,23 +26,23 @@ class sense_amp_test(openram_test): a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=4) self.local_check(a) - if OPTS.multiport_check: - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)") - a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=2) - self.local_check(a) + # check sense amp array for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)") + a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=2) + self.local_check(a) - debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 (multi-port case)") - a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=4) - self.local_check(a) + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 (multi-port case)") + a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=4) + self.local_check(a) globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/10_write_driver_array_test.py b/compiler/tests/10_write_driver_array_test.py index 7ede24dd..fa374181 100755 --- a/compiler/tests/10_write_driver_array_test.py +++ b/compiler/tests/10_write_driver_array_test.py @@ -17,6 +17,7 @@ class write_driver_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) import write_driver_array + # check write driver array for single port debug.info(2, "Testing write_driver_array for columns=8, word_size=8") a = write_driver_array.write_driver_array(columns=8, word_size=8) self.local_check(a) @@ -25,23 +26,23 @@ class write_driver_test(openram_test): a = write_driver_array.write_driver_array(columns=16, word_size=8) self.local_check(a) - if OPTS.multiport_check: - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)") - a = write_driver_array.write_driver_array(columns=8, word_size=8) - self.local_check(a) + # check write driver array for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)") + a = write_driver_array.write_driver_array(columns=8, word_size=8) + self.local_check(a) - debug.info(2, "Testing write_driver_array for columns=16, word_size=8 (multi-port case)") - a = write_driver_array.write_driver_array(columns=16, word_size=8) - self.local_check(a) + debug.info(2, "Testing write_driver_array for columns=16, word_size=8 (multi-port case)") + a = write_driver_array.write_driver_array(columns=16, word_size=8) + self.local_check(a) globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/11_dff_array_test.py b/compiler/tests/11_dff_array_test.py index a55c6407..eed41dda 100755 --- a/compiler/tests/11_dff_array_test.py +++ b/compiler/tests/11_dff_array_test.py @@ -31,7 +31,7 @@ class dff_array_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/11_dff_buf_array_test.py b/compiler/tests/11_dff_buf_array_test.py index f0b75552..d2932cac 100755 --- a/compiler/tests/11_dff_buf_array_test.py +++ b/compiler/tests/11_dff_buf_array_test.py @@ -31,7 +31,7 @@ class dff_buf_array_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/11_dff_buf_test.py b/compiler/tests/11_dff_buf_test.py index f434f768..c9c25f16 100755 --- a/compiler/tests/11_dff_buf_test.py +++ b/compiler/tests/11_dff_buf_test.py @@ -23,7 +23,7 @@ class dff_buf_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/12_tri_gate_array_test.py b/compiler/tests/12_tri_gate_array_test.py index 4f9cfa3e..cb789155 100755 --- a/compiler/tests/12_tri_gate_array_test.py +++ b/compiler/tests/12_tri_gate_array_test.py @@ -27,7 +27,7 @@ class tri_gate_array_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/13_delay_chain_test.py b/compiler/tests/13_delay_chain_test.py index 2cc745c2..1052f0de 100755 --- a/compiler/tests/13_delay_chain_test.py +++ b/compiler/tests/13_delay_chain_test.py @@ -23,7 +23,7 @@ class delay_chain_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/14_replica_bitline_multiport_test.py b/compiler/tests/14_replica_bitline_multiport_test.py new file mode 100755 index 00000000..41c3aa51 --- /dev/null +++ b/compiler/tests/14_replica_bitline_multiport_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +""" +Run a test on a multiport replica bitline +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class replica_bitline_multiport_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + import replica_bitline + + stages=4 + fanout=4 + rows=13 + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(2, "Testing 1rw 1r RBL with {0} FO4 stages, {1} rows".format(stages,rows)) + a = replica_bitline.replica_bitline(stages*[fanout],rows) + self.local_check(a) + + # check replica bitline in pbitcell multi-port + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell = "replica_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(2, "Testing RBL pbitcell 1rw with {0} FO4 stages, {1} rows".format(stages,rows)) + a = replica_bitline.replica_bitline(stages*[fanout],rows) + self.local_check(a) + + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 1 + + debug.info(2, "Testing RBL pbitcell 1rw 1w 1r with {0} FO4 stages, {1} rows".format(stages,rows)) + a = replica_bitline.replica_bitline(stages*[fanout],rows) + self.local_check(a) + + 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() diff --git a/compiler/tests/14_replica_bitline_test.py b/compiler/tests/14_replica_bitline_test.py index 6ecd612a..94a49f55 100755 --- a/compiler/tests/14_replica_bitline_test.py +++ b/compiler/tests/14_replica_bitline_test.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Run a test on a delay chain +Run a test on a replica bitline """ import unittest @@ -17,22 +17,25 @@ class replica_bitline_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) import replica_bitline + # check replica bitline in single port stages=4 fanout=4 rows=13 debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows)) - a = replica_bitline.replica_bitline(stages,fanout,rows) + a = replica_bitline.replica_bitline(stages*[fanout],rows) self.local_check(a) - + #debug.error("Exiting...", 1) + stages=8 rows=100 debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows)) - a = replica_bitline.replica_bitline(stages,fanout,rows) + a = replica_bitline.replica_bitline(stages*[fanout],rows) self.local_check(a) + globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/16_control_logic_test.py b/compiler/tests/16_control_logic_test.py index 3227a425..818c2eaf 100755 --- a/compiler/tests/16_control_logic_test.py +++ b/compiler/tests/16_control_logic_test.py @@ -18,13 +18,42 @@ class control_logic_test(openram_test): import control_logic import tech + # check control logic for single port debug.info(1, "Testing sample for control_logic") - a = control_logic.control_logic(num_rows=128) + a = control_logic.control_logic(num_rows=128, words_per_row=1) + self.local_check(a) + + # check control logic for multi-port + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell = "replica_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(1, "Testing sample for control_logic for multiport") + a = control_logic.control_logic(num_rows=128, words_per_row=1) + self.local_check(a) + + # Check port specific control logic + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 1 + + debug.info(1, "Testing sample for control_logic for multiport, only write control logic") + a = control_logic.control_logic(num_rows=128, words_per_row=1, port_type="rw") + self.local_check(a) + + debug.info(1, "Testing sample for control_logic for multiport, only write control logic") + a = control_logic.control_logic(num_rows=128, words_per_row=1, port_type="w") + self.local_check(a) + + debug.info(1, "Testing sample for control_logic for multiport, only read control logic") + a = control_logic.control_logic(num_rows=128, words_per_row=1, port_type="r") self.local_check(a) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/19_bank_select_test.py b/compiler/tests/19_bank_select_test.py index 0f4cb05f..1245926b 100755 --- a/compiler/tests/19_bank_select_test.py +++ b/compiler/tests/19_bank_select_test.py @@ -17,13 +17,30 @@ class bank_select_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) import bank_select - debug.info(1, "No column mux") - a = bank_select.bank_select() + debug.info(1, "No column mux, rw control logic") + a = bank_select.bank_select(port="rw") + self.local_check(a) + + OPTS.bitcell = "pbitcell" + debug.info(1, "No column mux, rw control logic") + a = bank_select.bank_select(port="rw") + self.local_check(a) + + OPTS.num_rw_ports = 0 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 1 + + debug.info(1, "No column mux, w control logic") + a = bank_select.bank_select(port="w") + self.local_check(a) + + debug.info(1, "No column mux, r control logic") + a = bank_select.bank_select(port="r") self.local_check(a) globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py index 9bab2a4f..0eff040d 100755 --- a/compiler/tests/19_multi_bank_test.py +++ b/compiler/tests/19_multi_bank_test.py @@ -11,6 +11,7 @@ import globals from globals import OPTS import debug +@unittest.skip("SKIPPING 19_multi_bank_test") class multi_bank_test(openram_test): def runTest(self): @@ -23,18 +24,21 @@ class multi_bank_test(openram_test): c.num_banks=2 c.words_per_row=1 + c.recompute_sizes() debug.info(1, "No column mux") a = bank(c, name="bank1_multi") self.local_check(a) c.num_words=32 c.words_per_row=2 + c.recompute_sizes() debug.info(1, "Two way column mux") a = bank(c, name="bank2_multi") self.local_check(a) c.num_words=64 c.words_per_row=4 + c.recompute_sizes() debug.info(1, "Four way column mux") a = bank(c, name="bank3_multi") self.local_check(a) @@ -42,13 +46,14 @@ class multi_bank_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 + c.recompute_sizes() debug.info(1, "Eight way column mux") a = bank(c, name="bank4_multi") self.local_check(a) globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/19_pmulti_bank_test.py b/compiler/tests/19_pmulti_bank_test.py new file mode 100755 index 00000000..32d3917a --- /dev/null +++ b/compiler/tests/19_pmulti_bank_test.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +@unittest.skip("SKIPPING 19_pmulti_bank_test") +class multi_bank_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from bank import bank + from sram_config import sram_config + OPTS.bitcell = "pbitcell" + + # testing layout of bank using pbitcell with 1 RW port (a 6T-cell equivalent) + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + c = sram_config(word_size=4, + num_words=16) + c.num_banks=2 + + c.words_per_row=1 + debug.info(1, "No column mux") + a = bank(c, name="bank1_multi") + self.local_check(a) + + c.num_words=32 + c.words_per_row=2 + debug.info(1, "Two way column mux") + a = bank(c, name="bank2_multi") + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 + debug.info(1, "Four way column mux") + a = bank(c, name="bank3_multi") + self.local_check(a) + + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + debug.info(1, "Eight way column mux") + a = bank(c, name="bank4_multi") + self.local_check(a) + + 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() diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py index 5ff00f1b..ff19ac15 100755 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -29,42 +29,48 @@ class psingle_bank_test(openram_test): OPTS.num_r_ports = 0 c = sram_config(word_size=4, num_words=16) + c.words_per_row=1 + c.recompute_sizes() debug.info(1, "No column mux") - a = bank(c, name="bank1_1rw_0w_0r_single") + name = "bank1_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) + a = bank(c, name=name) self.local_check(a) c.num_words=32 c.words_per_row=2 + c.recompute_sizes() debug.info(1, "Two way column mux") - a = bank(c, name="bank1_1rw_0w_0r_single") + name = "bank2_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) + a = bank(c, name=name) self.local_check(a) c.num_words=64 c.words_per_row=4 + c.recompute_sizes() debug.info(1, "Four way column mux") - a = bank(c, name="bank1_1rw_0w_0r_single") - self.local_check(a) - - c.num_words=128 - c.words_per_row=8 - debug.info(1, "Four way column mux") - a = bank(c, name="bank1_1rw_0w_0r_single") - self.local_check(a) - - """ - # multiport can't generate layout yet on the bank level - OPTS.netlist_only = True - - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 1 - OPTS.num_r_ports = 1 - - debug.info(1, "No column mux") - name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + name = "bank3_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) a = bank(c, name=name) self.local_check(a) + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + c.recompute_sizes() + debug.info(1, "Four way column mux") + name = "bank4_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) + a = bank(c, name=name) + self.local_check(a) + + + # testing bank using pbitcell in various port combinations + # layout for multiple ports does not work yet + """ + OPTS.netlist_only = True + + c.num_words=16 + c.words_per_row=1 + OPTS.num_rw_ports = c.num_rw_ports = 2 OPTS.num_w_ports = c.num_w_ports = 2 OPTS.num_r_ports = c.num_r_ports = 2 @@ -139,9 +145,9 @@ class psingle_bank_test(openram_test): self.local_check(a) """ - #globals.end_openram() + globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/19_single_bank_1rw_1r_test.py b/compiler/tests/19_single_bank_1rw_1r_test.py new file mode 100755 index 00000000..93bfd35a --- /dev/null +++ b/compiler/tests/19_single_bank_1rw_1r_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +""" +Run a regression test on 1rw 1r sram bank +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class single_bank_1rw_1r_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from bank import bank + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=16) + + c.words_per_row=1 + debug.info(1, "No column mux") + a = bank(c, name="bank1_single") + self.local_check(a) + + c.num_words=32 + c.words_per_row=2 + debug.info(1, "Two way column mux") + a = bank(c, name="bank2_single") + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 + debug.info(1, "Four way column mux") + a = bank(c, name="bank3_single") + self.local_check(a) + + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + debug.info(1, "Eight way column mux") + a = bank(c, name="bank4_single") + self.local_check(a) + + 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() diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py index 3c32b30d..e7179d96 100755 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_test.py @@ -38,7 +38,6 @@ class single_bank_test(openram_test): a = bank(c, name="bank3_single") self.local_check(a) - # Eight way has a short circuit of one column mux select to gnd rail c.word_size=2 c.num_words=128 c.words_per_row=8 @@ -48,7 +47,7 @@ class single_bank_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py new file mode 100755 index 00000000..f2f6386c --- /dev/null +++ b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete") +class psram_1bank_2mux_1rw_1w_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 0 + + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + c.num_words=32 + c.words_per_row=2 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + 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() diff --git a/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py new file mode 100755 index 00000000..3d049aef --- /dev/null +++ b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error") +class psram_1bank_2mux_1w_1r_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + + OPTS.num_rw_ports = 0 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 1 + + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + c.num_words=32 + c.words_per_row=2 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + 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() diff --git a/compiler/tests/20_psram_1bank_2mux_test.py b/compiler/tests/20_psram_1bank_2mux_test.py new file mode 100755 index 00000000..3afd2c9b --- /dev/null +++ b/compiler/tests/20_psram_1bank_2mux_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 20_psram_1bank_2mux_test, wide metal supply routing error") +class psram_1bank_2mux_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + + # testing layout of sram using pbitcell with 1 RW port (a 6T-cell equivalent) + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + c.num_words=32 + c.words_per_row=2 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + 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() diff --git a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py new file mode 100755 index 00000000..1be26ca7 --- /dev/null +++ b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class psram_1bank_4mux_1rw_1r_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 1 + + c = sram_config(word_size=4, + num_words=64, + num_banks=1) + c.num_words=64 + c.words_per_row=4 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + 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() diff --git a/compiler/tests/20_psram_1bank_test.py b/compiler/tests/20_psram_1bank_test.py deleted file mode 100755 index a1ccd8e2..00000000 --- a/compiler/tests/20_psram_1bank_test.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - -import unittest -from testutils import header,openram_test -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -from globals import OPTS -import debug - -@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete") -class sram_1bank_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram - from sram_config import sram_config - OPTS.bitcell = "pbitcell" - - # testing layout of bank using pbitcell with 1 RW port (a 6T-cell equivalent) - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - c = sram_config(word_size=4, - num_words=16, - num_banks=1) - c.words_per_row=1 - - debug.info(1, "Single bank, no column mux with control logic") - a = sram(c, "sram1") - self.local_check(a, final_verification=True) - - """ - OPTS.rw_ports = 1 - OPTS.w_ports = 1 - OPTS.r_ports = 1 - OPTS.netlist_only = True - - debug.info(1, "Single bank, no column mux with control logic") - a = sram(c, "sram1") - self.local_check(a, final_verification=True) - - c.num_words=32 - c.words_per_row=2 - debug.info(1, "Single bank two way column mux with control logic") - a = sram(c, "sram2") - self.local_check(a, final_verification=True) - - c.num_words=64 - c.words_per_row=4 - debug.info(1, "Single bank, four way column mux with control logic") - a = sram(c, "sram3") - self.local_check(a, final_verification=True) - - c.word_size=2 - c.num_words=128 - c.words_per_row=8 - debug.info(1, "Single bank, eight way column mux with control logic") - a = sram(c, "sram4") - self.local_check(a, final_verification=True) - """ - #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() diff --git a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py new file mode 100755 index 00000000..ea5fba78 --- /dev/null +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank, 2 port SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class sram_1bank_2mux_1rw_1r_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + + c.words_per_row=2 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + 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() diff --git a/compiler/tests/20_sram_1bank_2mux_test.py b/compiler/tests/20_sram_1bank_2mux_test.py new file mode 100755 index 00000000..26a7755f --- /dev/null +++ b/compiler/tests/20_sram_1bank_2mux_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 20_sram_1bank_2mux_test") +class sram_1bank_2mux_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + + c.words_per_row=2 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + 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() diff --git a/compiler/tests/20_sram_1bank_4mux_test.py b/compiler/tests/20_sram_1bank_4mux_test.py new file mode 100755 index 00000000..16654be5 --- /dev/null +++ b/compiler/tests/20_sram_1bank_4mux_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 20_sram_1bank_4mux_test") +class sram_1bank_4mux_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=64, + num_banks=1) + + c.words_per_row=4 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + 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() diff --git a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py new file mode 100755 index 00000000..dfd8a6a1 --- /dev/null +++ b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class sram_1bank_8mux_1rw_1r_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + c = sram_config(word_size=2, + num_words=128, + num_banks=1) + + c.words_per_row=8 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + 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() diff --git a/compiler/tests/20_sram_1bank_8mux_test.py b/compiler/tests/20_sram_1bank_8mux_test.py new file mode 100755 index 00000000..dde1a448 --- /dev/null +++ b/compiler/tests/20_sram_1bank_8mux_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 20_sram_1bank_8mux_test") +class sram_1bank_8mux_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=2, + num_words=128, + num_banks=1) + + c.words_per_row=8 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + 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() diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py new file mode 100755 index 00000000..02e82687 --- /dev/null +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank, 2 port SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class sram_1bank_nomux_1rw_1r_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + c = sram_config(word_size=4, + num_words=16, + num_banks=1) + + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + 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() diff --git a/compiler/tests/20_sram_1bank_nomux_test.py b/compiler/tests/20_sram_1bank_nomux_test.py new file mode 100755 index 00000000..7a03ce1e --- /dev/null +++ b/compiler/tests/20_sram_1bank_nomux_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 20_sram_1bank_nomux_test") +class sram_1bank_nomux_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + 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, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + 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() diff --git a/compiler/tests/20_sram_1bank_test.py b/compiler/tests/20_sram_1bank_test.py deleted file mode 100755 index ce482b6d..00000000 --- a/compiler/tests/20_sram_1bank_test.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - -import unittest -from testutils import header,openram_test -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -from globals import OPTS -import debug - -class sram_1bank_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram - from sram_config import sram_config - c = sram_config(word_size=4, - num_words=16, - num_banks=1) - - c.words_per_row=1 - debug.info(1, "Single bank, no column mux with control logic") - a = sram(c, "sram1") - self.local_check(a, final_verification=True) - - c.num_words=32 - c.words_per_row=2 - debug.info(1, "Single bank two way column mux with control logic") - a = sram(c, "sram2") - self.local_check(a, final_verification=True) - - c.num_words=64 - c.words_per_row=4 - debug.info(1, "Single bank, four way column mux with control logic") - a = sram(c, "sram3") - self.local_check(a, final_verification=True) - - c.word_size=2 - c.num_words=128 - c.words_per_row=8 - debug.info(1, "Single bank, eight way column mux with control logic") - a = sram(c, "sram4") - self.local_check(a, final_verification=True) - - 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() diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py index ff9fbaea..59db981d 100755 --- a/compiler/tests/20_sram_2bank_test.py +++ b/compiler/tests/20_sram_2bank_test.py @@ -23,18 +23,21 @@ class sram_2bank_test(openram_test): num_banks=2) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Two bank, no column mux with control logic") a = sram(c, "sram1") self.local_check(a, final_verification=True) c.num_words=64 c.words_per_row=2 + c.recompute_sizes() debug.info(1, "Two bank two way column mux with control logic") a = sram(c, "sram2") self.local_check(a, final_verification=True) c.num_words=128 c.words_per_row=4 + c.recompute_sizes() debug.info(1, "Two bank, four way column mux with control logic") a = sram(c, "sram3") self.local_check(a, final_verification=True) @@ -42,13 +45,14 @@ class sram_2bank_test(openram_test): c.word_size=2 c.num_words=256 c.words_per_row=8 + c.recompute_sizes() debug.info(1, "Two bank, eight way column mux with control logic") a = sram(c, "sram4") self.local_check(a, final_verification=True) globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/20_sram_4bank_test.py b/compiler/tests/20_sram_4bank_test.py deleted file mode 100755 index fb34d3b0..00000000 --- a/compiler/tests/20_sram_4bank_test.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 -""" -Run a regression test on a 4 bank SRAM -""" - -import unittest -from testutils import header,openram_test -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -from globals import OPTS -import debug - -@unittest.skip("Multibank is not working yet.") -class sram_4bank_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram - from sram_config import sram_config - c = sram_config(word_size=16, - num_words=64, - num_banks=4) - - debug.info(1, "Four bank, no column mux with control logic") - a = sram(c, "sram1") - self.local_check(a, final_verification=True) - - c.num_words=128 - c.words_per_row=2 - debug.info(1, "Four bank two way column mux with control logic") - a = sram(c, "sram2") - self.local_check(a, final_verification=True) - - c.num_words=256 - c.words_per_row=4 - debug.info(1, "Four bank, four way column mux with control logic") - a = sram(c, "sram3") - self.local_check(a, final_verification=True) - - c.word_size=2 - c.num_words=256 - c.words_per_row=8 - debug.info(1, "Four bank, eight way column mux with control logic") - a = sram.sram(c, "sram4") - self.local_check(a, final_verification=True) - - 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() diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index da59c447..edbd6a55 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -17,22 +17,20 @@ class timing_sram_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) OPTS.spice_name="hspice" 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 - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - + from characterizer import delay, bitline_delay from sram import sram from sram_config import sram_config c = sram_config(word_size=1, 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 = sram(c, name="sram1") @@ -45,34 +43,37 @@ class timing_sram_test(openram_test): corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) d = delay(s.s, tempspice, corner) + bl = bitline_delay(s.s, tempspice, corner) import tech loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] - data = d.analyze(probe_address, probe_data, slews, loads) - - #Assumes single rw port (6t sram) + data, port_data = d.analyze(probe_address, probe_data, slews, loads) + #bitline_swing = bl.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_hl0': [2.5829000000000004], - 'delay_lh0': [0.2255964], - 'leakage_power': 0.0019498999999999996, - 'min_period': 4.844, - 'read0_power0': [0.055371399999999994], - 'read1_power0': [0.0520225], - 'slew_hl0': [0.0794261], - 'slew_lh0': [0.0236264], - 'write0_power0': [0.06545659999999999], - 'write1_power0': [0.057846299999999996]} + golden_data = {'delay_hl': [0.2011], + 'delay_lh': [0.2011], + 'leakage_power': 0.002, + 'min_period': 0.41, + 'read0_power': [0.63604], + 'read1_power': [0.6120599999999999], + 'slew_hl': [0.10853], + 'slew_lh': [0.10853], + 'write0_power': [0.51742], + 'write1_power': [0.51095]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_hl0': [3.452], - 'delay_lh0': [1.3792000000000002], - 'leakage_power': 0.0257065, - 'min_period': 4.688, - 'read0_power0': [15.0755], - 'read1_power0': [14.4526], - 'slew_hl0': [0.6137363], - 'slew_lh0': [0.3381045], - 'write0_power0': [16.9203], - 'write1_power0': [15.367]} + golden_data = {'delay_hl': [1.3911], + 'delay_lh': [1.3911], + 'leakage_power': 0.0278488, + 'min_period': 2.812, + 'read0_power': [22.1183], + 'read1_power': [21.4388], + 'slew_hl': [0.7397553], + 'slew_lh': [0.7397553], + 'write0_power': [19.4103], + 'write1_power': [20.1167]} else: self.assertTrue(False) # other techs fail # Check if no too many or too few results @@ -82,7 +83,7 @@ class timing_sram_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/21_hspice_setuphold_test.py b/compiler/tests/21_hspice_setuphold_test.py index 2969f95e..c003f54a 100755 --- a/compiler/tests/21_hspice_setuphold_test.py +++ b/compiler/tests/21_hspice_setuphold_test.py @@ -17,15 +17,13 @@ class timing_setup_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) OPTS.spice_name="hspice" 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 - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram import tech slews = [tech.spice["rise_time"]*2] @@ -35,7 +33,7 @@ class timing_setup_test(openram_test): data = sh.analyze(slews,slews) #print data if OPTS.tech_name == "freepdk45": - golden_data = {'hold_times_HL': [-0.0097656], + golden_data = {'hold_times_HL': [-0.0158691], 'hold_times_LH': [-0.0158691], 'setup_times_HL': [0.026855499999999997], 'setup_times_LH': [0.032959]} @@ -54,7 +52,7 @@ class timing_setup_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index cf7f096b..20ba14cc 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -17,22 +17,20 @@ 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 + 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 - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - from sram import sram from sram_config import sram_config c = sram_config(word_size=1, 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 = sram(c, name="sram1") @@ -48,30 +46,32 @@ class timing_sram_test(openram_test): import tech loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] - data = d.analyze(probe_address, probe_data, slews, loads) + 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_hl0': [2.584251], - 'delay_lh0': [0.22870469999999998], - 'leakage_power': 0.0009567935, - 'min_period': 4.844, - 'read0_power0': [0.0547588], - 'read1_power0': [0.051159970000000006], - 'slew_hl0': [0.08164099999999999], - 'slew_lh0': [0.025474979999999998], - 'write0_power0': [0.06513271999999999], - 'write1_power0': [0.058057000000000004]} + golden_data = {'delay_hl': [0.20443139999999999], + 'delay_lh': [0.20443139999999999], + 'leakage_power': 0.0017840640000000001, + 'min_period': 0.41, + 'read0_power': [0.6435831], + 'read1_power': [0.6233463], + 'slew_hl': [0.1138734], + 'slew_lh': [0.1138734], + 'write0_power': [0.5205761], + 'write1_power': [0.5213689]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_hl0': [3.644147], - 'delay_lh0': [1.629815], - 'leakage_power': 0.0009299118999999999, - 'min_period': 4.688, - 'read0_power0': [16.28732], - 'read1_power0': [15.75155], - 'slew_hl0': [0.6722473], - 'slew_lh0': [0.3386347], - 'write0_power0': [18.545450000000002], - 'write1_power0': [16.81084]} + golden_data = {'delay_hl': [1.610911], + 'delay_lh': [1.610911], + 'leakage_power': 0.0023593859999999998, + 'min_period': 3.281, + 'read0_power': [20.763569999999998], + 'read1_power': [20.32745], + 'slew_hl': [0.7986348999999999], + 'slew_lh': [0.7986348999999999], + 'write0_power': [17.58272], + 'write1_power': [18.523419999999998]} else: self.assertTrue(False) # other techs fail @@ -82,7 +82,7 @@ class timing_sram_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/21_ngspice_setuphold_test.py b/compiler/tests/21_ngspice_setuphold_test.py index d86fcb23..924d05a5 100755 --- a/compiler/tests/21_ngspice_setuphold_test.py +++ b/compiler/tests/21_ngspice_setuphold_test.py @@ -17,15 +17,13 @@ class timing_setup_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) OPTS.spice_name="ngspice" 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 - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram import tech slews = [tech.spice["rise_time"]*2] @@ -55,7 +53,7 @@ class timing_setup_test(openram_test): reload(characterizer) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/22_psram_1bank_2mux_func_test.py b/compiler/tests/22_psram_1bank_2mux_func_test.py new file mode 100755 index 00000000..87c8db48 --- /dev/null +++ b/compiler/tests/22_psram_1bank_2mux_func_test.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 22_psram_1bank_2mux_1rw_1r_1w_func_test, third port reads are broken?") +class psram_1bank_2mux_1rw_1r_1w_func_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional, delay + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=64, + num_banks=1) + c.words_per_row=2 + c.recompute_sizes() + debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = sram(c, name="sram") + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + d = delay(s.s, tempspice, corner) + feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) + + f.num_cycles = 10 + (fail, error) = f.run() + self.assertTrue(fail,error) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/22_psram_1bank_4mux_func_test.py b/compiler/tests/22_psram_1bank_4mux_func_test.py new file mode 100755 index 00000000..16661483 --- /dev/null +++ b/compiler/tests/22_psram_1bank_4mux_func_test.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test, third port reads are broken?") +class psram_1bank_4mux_func_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional, delay + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=256, + num_banks=1) + c.words_per_row=4 + c.recompute_sizes() + debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = sram(c, name="sram") + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + d = delay(s.s, tempspice, corner) + feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) + + f.num_cycles = 10 + (fail, error) = f.run() + self.assertTrue(fail,error) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/22_psram_1bank_8mux_func_test.py b/compiler/tests/22_psram_1bank_8mux_func_test.py new file mode 100755 index 00000000..86cfd16f --- /dev/null +++ b/compiler/tests/22_psram_1bank_8mux_func_test.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 22_psram_1bank_8mux_func_test") +class psram_1bank_8mux_func_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional, delay + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=256, + num_banks=1) + c.words_per_row=8 + c.recompute_sizes() + debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = sram(c, name="sram") + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + d = delay(s.s, tempspice, corner) + feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) + + f.num_cycles = 10 + (fail, error) = f.run() + self.assertTrue(fail,error) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/22_psram_1bank_nomux_func_test.py b/compiler/tests/22_psram_1bank_nomux_func_test.py new file mode 100755 index 00000000..3133eb03 --- /dev/null +++ b/compiler/tests/22_psram_1bank_nomux_func_test.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +""" +Run a functioal test on 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 22_psram_1bank_nomux_func_test") +class psram_1bank_nomux_func_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional, delay + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = sram(c, name="sram") + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + d = delay(s.s, tempspice, corner) + feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) + + f.num_cycles = 10 + (fail, error) = f.run() + self.assertTrue(fail,error) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/22_sram_1bank_2mux_func_test.py b/compiler/tests/22_sram_1bank_2mux_func_test.py new file mode 100755 index 00000000..f163216e --- /dev/null +++ b/compiler/tests/22_sram_1bank_2mux_func_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 22_sram_1bank_2mux_func_test") +class sram_1bank_2mux_func_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional, delay + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=64, + num_banks=1) + c.words_per_row=2 + c.recompute_sizes() + debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = sram(c, name="sram") + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + d = delay(s.s, tempspice, corner) + feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) + + f.num_cycles = 10 + (fail, error) = f.run(feasible_period) + self.assertTrue(fail,error) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/22_sram_1bank_4mux_func_test.py b/compiler/tests/22_sram_1bank_4mux_func_test.py new file mode 100755 index 00000000..049d8459 --- /dev/null +++ b/compiler/tests/22_sram_1bank_4mux_func_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 22_sram_1bank_4mux_func_test") +class sram_1bank_4mux_func_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional, delay + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=256, + num_banks=1) + c.words_per_row=4 + c.recompute_sizes() + debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = sram(c, name="sram") + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + d = delay(s.s, tempspice, corner) + feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) + + f.num_cycles = 10 + (fail, error) = f.run() + self.assertTrue(fail,error) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/22_sram_1bank_8mux_func_test.py b/compiler/tests/22_sram_1bank_8mux_func_test.py new file mode 100755 index 00000000..d159f265 --- /dev/null +++ b/compiler/tests/22_sram_1bank_8mux_func_test.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 22_sram_1bank_8mux_func_test") +class sram_1bank_8mux_func_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional, delay + if not OPTS.spice_exe: + debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) + + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=256, + num_banks=1) + c.words_per_row=8 + c.recompute_sizes() + debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = sram(c, name="sram") + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + d = delay(s.s, tempspice, corner) + feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) + + f.num_cycles = 10 + (fail, error) = f.run() + self.assertTrue(fail,error) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/22_sram_1bank_nomux_func_test.py b/compiler/tests/22_sram_1bank_nomux_func_test.py new file mode 100755 index 00000000..08a21d92 --- /dev/null +++ b/compiler/tests/22_sram_1bank_nomux_func_test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +""" +Run a functioal test on 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 22_sram_func_test") +class sram_1bank_nomux_func_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.analytical_delay = False + OPTS.netlist_only = True + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = sram(c, name="sram") + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + + f = functional(s.s, tempspice, corner) + f.num_cycles = 10 + (fail, error) = f.run() + self.assertTrue(fail,error) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py b/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py new file mode 100755 index 00000000..a609460b --- /dev/null +++ b/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +""" +Run a functioal test on 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 22_sram_1rw_1r_1bank_nomux_func_test") +class psram_1bank_nomux_func_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 1 + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional, delay + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Functional test for sram 1rw,1r with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = sram(c, name="sram") + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + d = delay(s.s, tempspice, corner) + feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) + + f.num_cycles = 10 + (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() diff --git a/compiler/tests/22_sram_func_test.py b/compiler/tests/22_sram_func_test.py deleted file mode 100755 index 22443c5a..00000000 --- a/compiler/tests/22_sram_func_test.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - -import unittest -from testutils import header,openram_test -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -from globals import OPTS -import debug - -@unittest.skip("SKIPPING 22_sram_func_test") -class sram_func_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - OPTS.analytical_delay = False - - # This is a hack to reload the characterizer __init__ with the spice version - from importlib import reload - import characterizer - reload(characterizer) - from characterizer import delay - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - - import sram - - debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") - s = sram.sram(word_size=1, - num_words=16, - num_banks=1, - name="sram_func_test") - - tempspice = OPTS.openram_temp + "temp.sp" - s.sp_write(tempspice) - - probe_address = "1" * s.addr_size - probe_data = s.word_size - 1 - debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) - - corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - d = delay(s,tempspice,corner) - d.set_probe(probe_address,probe_data) - - # This will exit if it doesn't find a feasible period - import tech - d.load = tech.spice["msflop_in_cap"]*4 - d.slew = tech.spice["rise_time"]*2 - feasible_period = d.find_feasible_period() - - os.remove(tempspice) - - reload(characterizer) - globals.end_openram() - -# instantiate a copdsay of the class to actually run the test -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main() diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py index 8d5064d5..8a996cb4 100755 --- a/compiler/tests/23_lib_sram_model_test.py +++ b/compiler/tests/23_lib_sram_model_test.py @@ -23,6 +23,7 @@ class lib_test(openram_test): num_words=16, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) tempspice = OPTS.openram_temp + "temp.sp" @@ -44,7 +45,7 @@ class lib_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/23_lib_sram_prune_test.py b/compiler/tests/23_lib_sram_prune_test.py index 952072aa..1b93d1fd 100755 --- a/compiler/tests/23_lib_sram_prune_test.py +++ b/compiler/tests/23_lib_sram_prune_test.py @@ -32,6 +32,7 @@ class lib_test(openram_test): num_words=16, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Testing pruned timing for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) @@ -55,7 +56,7 @@ class lib_test(openram_test): reload(characterizer) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py index 1b2d317c..1c26fd45 100755 --- a/compiler/tests/23_lib_sram_test.py +++ b/compiler/tests/23_lib_sram_test.py @@ -32,6 +32,7 @@ class lib_test(openram_test): num_words=16, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) @@ -54,7 +55,7 @@ class lib_test(openram_test): reload(characterizer) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/24_lef_sram_test.py b/compiler/tests/24_lef_sram_test.py index f66104b6..983038a2 100755 --- a/compiler/tests/24_lef_sram_test.py +++ b/compiler/tests/24_lef_sram_test.py @@ -23,6 +23,7 @@ class lef_test(openram_test): num_words=16, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Testing LEF for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) @@ -35,14 +36,11 @@ class lef_test(openram_test): # let's diff the result with a golden model golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),leffile) - self.isdiff(lefname,golden) - - os.system("rm {0}".format(gdsname)) - os.system("rm {0}".format(lefname)) + self.assertTrue(self.isdiff(lefname,golden)) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py index 48ba29e8..f98475b4 100755 --- a/compiler/tests/25_verilog_sram_test.py +++ b/compiler/tests/25_verilog_sram_test.py @@ -22,6 +22,7 @@ class verilog_test(openram_test): num_words=16, num_banks=1) c.words_per_row=1 + c.recompute_sizes() debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) @@ -32,13 +33,11 @@ class verilog_test(openram_test): # let's diff the result with a golden model golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),vfile) - self.isdiff(vname,golden) - - os.system("rm {0}".format(vname)) + self.assertTrue(self.isdiff(vname,golden)) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/22_pex_test.py b/compiler/tests/26_pex_test.py similarity index 99% rename from compiler/tests/22_pex_test.py rename to compiler/tests/26_pex_test.py index 7755a2c7..d374e485 100755 --- a/compiler/tests/22_pex_test.py +++ b/compiler/tests/26_pex_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 22_sram_pex_test") +@unittest.skip("SKIPPING 26_pex_test") class sram_func_test(openram_test): def runTest(self): @@ -306,7 +306,7 @@ class sram_func_test(openram_test): sti_file.file.close() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/27_worst_case_delay_test.py b/compiler/tests/27_worst_case_delay_test.py new file mode 100755 index 00000000..834c6b69 --- /dev/null +++ b/compiler/tests/27_worst_case_delay_test.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +@unittest.skip("SKIPPING 27_worst_case_delay_test") +class worst_case_timing_sram_test(openram_test): + + def runTest(self): + OPTS.tech_name = "freepdk45" + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.spice_name="hspice" + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + OPTS.check_lvsdrc = 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 worst_case + if not OPTS.spice_exe: + debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) + + word_size, num_words, num_banks = 2, 16, 1 + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=word_size, + num_words=num_words, + num_banks=num_banks) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Testing the timing different bitecells inside a {}bit, {} words SRAM with {} bank".format( + word_size, num_words, num_banks)) + s = sram(c, name="sram1") + + sp_netlist_file = OPTS.openram_temp + "temp.sp" + s.sp_write(sp_netlist_file) + + if OPTS.use_pex: + gdsname = OPTS.output_path + s.name + ".gds" + s.gds_write(gdsname) + + import verify + reload(verify) + # Output the extracted design if requested + sp_pex_file = OPTS.output_path + s.name + "_pex.sp" + verify.run_pex(s.name, gdsname, sp_netlist_file, output=sp_pex_file) + sp_sim_file = sp_pex_file + debug.info(1, "Performing spice simulations with backannotated spice file.") + else: + sp_sim_file = sp_netlist_file + debug.info(1, "Performing spice simulations with spice netlist.") + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + wc = worst_case(s.s, sp_sim_file, corner) + import tech + loads = [tech.spice["msflop_in_cap"]*4] + slews = [tech.spice["rise_time"]*2] + probe_address = "1" * s.s.addr_size + probe_data = s.s.word_size - 1 + wc.analyze(probe_address, probe_data, slews, loads) + + 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() diff --git a/compiler/tests/30_openram_test.py b/compiler/tests/30_openram_test.py index 81864840..55ab9457 100755 --- a/compiler/tests/30_openram_test.py +++ b/compiler/tests/30_openram_test.py @@ -17,7 +17,8 @@ import getpass class openram_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) + globals.init_openram("{0}/tests/config_20_{1}".format(OPENRAM_HOME,OPTS.tech_name)) debug.info(1, "Testing top-level openram.py with 2-bit, 16 word SRAM.") out_file = "testsram" @@ -41,14 +42,19 @@ class openram_test(openram_test): verbosity += " -v" - OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) - - cmd = "python3 {0}/openram.py -n -o {1} -p {2} {3} config_20_{4}.py 2>&1 > {5}/output.log".format(OPENRAM_HOME, - out_file, - out_path, - verbosity, - OPTS.tech_name, - out_path) + # Always perform code coverage + if OPTS.coverage == 0: + debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") + exe_name = "{0}/openram.py ".format(OPENRAM_HOME) + else: + exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) + config_name = "{0}config_20_{1}.py".format(OPENRAM_HOME + "/tests/",OPTS.tech_name) + cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, + out_file, + out_path, + verbosity, + config_name, + out_path) debug.info(1, cmd) os.system(cmd) @@ -62,7 +68,12 @@ class openram_test(openram_test): import glob files = glob.glob('{0}/*.lib'.format(out_path)) self.assertTrue(len(files)>0) - + + # Make sure there is any .html file + if os.path.exists(out_path): + datasheets = glob.glob('{0}/*html'.format(out_path)) + self.assertTrue(len(datasheets)>0) + # grep any errors from the output output_log = open("{0}/output.log".format(out_path),"r") output = output_log.read() @@ -78,7 +89,7 @@ class openram_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/config_20_freepdk45.py b/compiler/tests/config_20_freepdk45.py old mode 100644 new mode 100755 diff --git a/compiler/tests/config_20_scn3me_subm.py b/compiler/tests/config_20_scn3me_subm.py old mode 100644 new mode 100755 diff --git a/compiler/tests/config_20_scn4m_subm.py b/compiler/tests/config_20_scn4m_subm.py old mode 100644 new mode 100755 diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45.v b/compiler/tests/golden/sram_2_16_1_freepdk45.v index 94bd09e2..025350bc 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45.v +++ b/compiler/tests/golden/sram_2_16_1_freepdk45.v @@ -2,46 +2,60 @@ // Words: 16 // Word size: 2 -module sram_2_16_1_freepdk45(DATA,ADDR,CSb,WEb,OEb,clk); +module sram_2_16_1_freepdk45( +// Port 0: RW + clk0,csb0,web0,ADDR0,DIN0,DOUT0 + ); parameter DATA_WIDTH = 2 ; parameter ADDR_WIDTH = 4 ; parameter RAM_DEPTH = 1 << ADDR_WIDTH; + // FIXME: This delay is arbitrary. parameter DELAY = 3 ; - inout [DATA_WIDTH-1:0] DATA; - input [ADDR_WIDTH-1:0] ADDR; - input CSb; // active low chip select - input WEb; // active low write control - input OEb; // active output enable - input clk; // clock + input clk0; // clock + input csb0; // active low chip select + input web0; // active low write control + input [ADDR_WIDTH-1:0] ADDR0; + input [DATA_WIDTH-1:0] DIN0; + output [DATA_WIDTH-1:0] DOUT0; - reg [DATA_WIDTH-1:0] data_out ; - reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; + reg csb0_reg; + reg web0_reg; + reg [ADDR_WIDTH-1:0] ADDR0_reg; + reg [DATA_WIDTH-1:0] DIN0_reg; + reg [DATA_WIDTH-1:0] DOUT0; - // Tri-State Buffer control - // output : When WEb = 1, oeb = 0, csb = 0 - assign DATA = (!CSb && !OEb && WEb) ? data_out : 2'bz; - - // Memory Write Block - // Write Operation : When WEb = 0, CSb = 0 - always @ (posedge clk) - begin : MEM_WRITE - if ( !CSb && !WEb ) begin - mem[ADDR] = DATA; - $display($time," Writing %m ABUS=%b DATA=%b",ADDR,DATA); - end + // All inputs are registers + always @(posedge clk0) + begin + csb0_reg = csb0; + web0_reg = web0; + ADDR0_reg = ADDR0; + DIN0_reg = DIN0; + DOUT0 = 2'bx; + if ( !csb0_reg && web0_reg ) + $display($time," Reading %m ADDR0=%b DOUT0=%b",ADDR0_reg,mem[ADDR0_reg]); + if ( !csb0_reg && !web0_reg ) + $display($time," Writing %m ADDR0=%b DIN0=%b",ADDR0_reg,DIN0_reg); end +reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; - // Memory Read Block - // Read Operation : When WEb = 1, CSb = 0 - always @ (posedge clk) - begin : MEM_READ - if (!CSb && WEb) begin - data_out <= #(DELAY) mem[ADDR]; - $display($time," Reading %m ABUS=%b DATA=%b",ADDR,mem[ADDR]); - end + // Memory Write Block Port 0 + // Write Operation : When web0 = 0, csb0 = 0 + always @ (negedge clk0) + begin : MEM_WRITE0 + if ( !csb0_reg && !web0_reg ) + mem[ADDR0_reg] = DIN0_reg; + end + + // Memory Read Block Port 0 + // Read Operation : When web0 = 1, csb0 = 0 + always @ (negedge clk0) + begin : MEM_READ0 + if (!csb0_reg && web0_reg) + DOUT0 <= #(DELAY) mem[ADDR0_reg]; end endmodule 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 84f301f8..fc8d2be3 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,214 +78,216 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 948.52275; + area : 977.4951374999999; leakage_power () { - when : "CSb"; - value : 0.0021292; + when : "CSb0"; + value : 0.0011164579999999999; } cell_leakage_power : 0; - bus(DIN){ + bus(DIN0){ bus_type : DATA; direction : input; capacitance : 0.2091; memory_write(){ - address : ADDR; - clocked_on : clk; + address : ADDR0; + clocked_on : clk0; + } + pin(DIN0){ + timing(){ + timing_type : setup_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); + } + } } } - bus(DOUT){ + bus(DOUT0){ bus_type : DATA; direction : output; max_capacitance : 1.6728; min_capacitance : 0.052275; memory_read(){ - address : ADDR; - } - pin(DOUT[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); - } + address : ADDR0; } + pin(DOUT0){ timing(){ timing_sense : non_unate; - related_pin : "clk"; - timing_type : rising_edge; + related_pin : "clk0"; + timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("0.229, 0.23, 0.234",\ - "0.23, 0.23, 0.234",\ - "0.236, 0.236, 0.24"); + values("0.235, 0.235, 0.239",\ + "0.235, 0.236, 0.24",\ + "0.241, 0.242, 0.246"); } cell_fall(CELL_TABLE) { - values("2.555, 2.556, 2.568",\ - "2.555, 2.557, 2.569",\ - "2.562, 2.563, 2.575"); + values("2.583, 2.585, 2.612",\ + "2.584, 2.585, 2.613",\ + "2.59, 2.592, 2.62"); } rise_transition(CELL_TABLE) { - values("0.02, 0.021, 0.028",\ - "0.02, 0.021, 0.028",\ - "0.02, 0.021, 0.028"); + values("0.022, 0.022, 0.03",\ + "0.022, 0.023, 0.03",\ + "0.022, 0.022, 0.03"); } fall_transition(CELL_TABLE) { - values("0.111, 0.112, 0.115",\ - "0.111, 0.111, 0.115",\ - "0.111, 0.111, 0.116"); + values("0.078, 0.079, 0.083",\ + "0.078, 0.079, 0.083",\ + "0.079, 0.079, 0.083"); } } } } - bus(ADDR){ + bus(ADDR0){ bus_type : ADDR; direction : input; capacitance : 0.2091; max_transition : 0.04; - pin(ADDR[3:0]){ + pin(ADDR0){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); } } } } - pin(CSb){ + pin(CSb0){ direction : input; capacitance : 0.2091; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); } } } - pin(WEb){ + pin(WEb0){ direction : input; capacitance : 0.2091; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); } } } - pin(clk){ + pin(clk0){ clock : true; direction : input; capacitance : 0.2091; internal_power(){ - when : "!CSb & clk & !WEb"; + when : "!CSb0 & clk0 & !WEb0"; rise_power(scalar){ - values("0.027431397222222223"); + values("0.03599689694444445"); } fall_power(scalar){ - values("0.027431397222222223"); + values("0.03599689694444445"); } } internal_power(){ - when : "!CSb & !clk & WEb"; + when : "!CSb0 & !clk0 & WEb0"; rise_power(scalar){ - values("0.026240397222222222"); + values("0.029906643888888886"); } fall_power(scalar){ - values("0.026240397222222222"); + values("0.029906643888888886"); } } internal_power(){ - when : "CSb"; + when : "CSb0"; rise_power(scalar){ values("0"); } @@ -295,7 +297,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type :"min_pulse_width"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("2.422"); } @@ -305,7 +307,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type :"minimum_period"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("4.844"); } @@ -314,5 +316,6 @@ cell (sram_2_16_1_freepdk45){ } } } + } } 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 2fbbd8b8..afb22057 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,96 +78,98 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 948.52275; + area : 977.4951374999999; leakage_power () { - when : "CSb"; - value : 0.000168; + when : "CSb0"; + value : 0.000179; } cell_leakage_power : 0; - bus(DIN){ + bus(DIN0){ bus_type : DATA; direction : input; capacitance : 0.2091; memory_write(){ - address : ADDR; - clocked_on : clk; + address : ADDR0; + clocked_on : clk0; + } + pin(DIN0){ + timing(){ + timing_type : setup_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + } } } - bus(DOUT){ + bus(DOUT0){ bus_type : DATA; direction : output; max_capacitance : 1.6728; min_capacitance : 0.052275; memory_read(){ - address : ADDR; - } - pin(DOUT[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } + address : ADDR0; } + pin(DOUT0){ timing(){ timing_sense : non_unate; - related_pin : "clk"; - timing_type : rising_edge; + related_pin : "clk0"; + timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("0.103, 0.104, 0.113",\ - "0.103, 0.104, 0.113",\ - "0.103, 0.104, 0.113"); + values("0.098, 0.098, 0.098",\ + "0.098, 0.098, 0.098",\ + "0.098, 0.098, 0.098"); } cell_fall(CELL_TABLE) { - values("0.103, 0.104, 0.113",\ - "0.103, 0.104, 0.113",\ - "0.103, 0.104, 0.113"); + values("0.098, 0.098, 0.098",\ + "0.098, 0.098, 0.098",\ + "0.098, 0.098, 0.098"); } rise_transition(CELL_TABLE) { - values("0.006, 0.007, 0.018",\ - "0.006, 0.007, 0.018",\ - "0.006, 0.007, 0.018"); + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } fall_transition(CELL_TABLE) { - values("0.006, 0.007, 0.018",\ - "0.006, 0.007, 0.018",\ - "0.006, 0.007, 0.018"); + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } } } - bus(ADDR){ + bus(ADDR0){ bus_type : ADDR; direction : input; capacitance : 0.2091; max_transition : 0.04; - pin(ADDR[3:0]){ + pin(ADDR0){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -181,7 +183,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -196,12 +198,12 @@ cell (sram_2_16_1_freepdk45){ } } - pin(CSb){ + pin(CSb0){ direction : input; capacitance : 0.2091; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -215,7 +217,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -229,12 +231,12 @@ cell (sram_2_16_1_freepdk45){ } } - pin(WEb){ + pin(WEb0){ direction : input; capacitance : 0.2091; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -248,7 +250,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -262,30 +264,30 @@ cell (sram_2_16_1_freepdk45){ } } - pin(clk){ + pin(clk0){ clock : true; direction : input; capacitance : 0.2091; internal_power(){ - when : "!CSb & clk & !WEb"; + when : "!CSb0 & clk0 & !WEb0"; rise_power(scalar){ - values("0.0739870044551111"); + values("0.0747594982142222"); } fall_power(scalar){ - values("0.0739870044551111"); + values("0.0747594982142222"); } } internal_power(){ - when : "!CSb & !clk & WEb"; + when : "!CSb0 & !clk0 & WEb0"; rise_power(scalar){ - values("0.0739870044551111"); + values("0.0747594982142222"); } fall_power(scalar){ - values("0.0739870044551111"); + values("0.0747594982142222"); } } internal_power(){ - when : "CSb"; + when : "CSb0"; rise_power(scalar){ values("0"); } @@ -295,7 +297,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type :"min_pulse_width"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("0.0"); } @@ -305,7 +307,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type :"minimum_period"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("0"); } @@ -314,5 +316,6 @@ cell (sram_2_16_1_freepdk45){ } } } + } } 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 a3ec121c..b20f10be 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,214 +78,216 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 948.52275; + area : 977.4951374999999; leakage_power () { - when : "CSb"; - value : 0.0021292; + when : "CSb0"; + value : 0.0011164579999999999; } cell_leakage_power : 0; - bus(DIN){ + bus(DIN0){ bus_type : DATA; direction : input; capacitance : 0.2091; memory_write(){ - address : ADDR; - clocked_on : clk; + address : ADDR0; + clocked_on : clk0; + } + pin(DIN0){ + timing(){ + timing_type : setup_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); + } + } } } - bus(DOUT){ + bus(DOUT0){ bus_type : DATA; direction : output; max_capacitance : 1.6728; min_capacitance : 0.052275; memory_read(){ - address : ADDR; - } - pin(DOUT[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); - } + address : ADDR0; } + pin(DOUT0){ timing(){ timing_sense : non_unate; - related_pin : "clk"; - timing_type : rising_edge; + related_pin : "clk0"; + timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("0.227, 0.227, 0.231",\ - "0.227, 0.228, 0.232",\ - "0.233, 0.234, 0.238"); + values("0.233, 0.233, 0.237",\ + "0.233, 0.234, 0.237",\ + "0.239, 0.24, 0.244"); } cell_fall(CELL_TABLE) { - values("2.555, 2.557, 2.569",\ - "2.556, 2.557, 2.569",\ - "2.562, 2.563, 2.576"); + values("2.584, 2.585, 2.611",\ + "2.584, 2.585, 2.612",\ + "2.591, 2.592, 2.618"); } rise_transition(CELL_TABLE) { - values("0.02, 0.021, 0.028",\ - "0.02, 0.021, 0.028",\ - "0.02, 0.021, 0.028"); + values("0.022, 0.022, 0.03",\ + "0.022, 0.023, 0.03",\ + "0.022, 0.023, 0.03"); } fall_transition(CELL_TABLE) { - values("0.11, 0.11, 0.114",\ - "0.109, 0.11, 0.113",\ - "0.11, 0.11, 0.114"); + values("0.076, 0.077, 0.082",\ + "0.077, 0.077, 0.082",\ + "0.077, 0.077, 0.082"); } } } } - bus(ADDR){ + bus(ADDR0){ bus_type : ADDR; direction : input; capacitance : 0.2091; max_transition : 0.04; - pin(ADDR[3:0]){ + pin(ADDR0){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); } } } } - pin(CSb){ + pin(CSb0){ direction : input; capacitance : 0.2091; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); } } } - pin(WEb){ + pin(WEb0){ direction : input; capacitance : 0.2091; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); } } } - pin(clk){ + pin(clk0){ clock : true; direction : input; capacitance : 0.2091; internal_power(){ - when : "!CSb & clk & !WEb"; + when : "!CSb0 & clk0 & !WEb0"; rise_power(scalar){ - values("0.025181683333333333"); + values("0.03334771594444444"); } fall_power(scalar){ - values("0.025181683333333333"); + values("0.03334771594444444"); } } internal_power(){ - when : "!CSb & !clk & WEb"; + when : "!CSb0 & !clk0 & WEb0"; rise_power(scalar){ - values("0.024945991666666667"); + values("0.028457026222222223"); } fall_power(scalar){ - values("0.024945991666666667"); + values("0.028457026222222223"); } } internal_power(){ - when : "CSb"; + when : "CSb0"; rise_power(scalar){ values("0"); } @@ -295,7 +297,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type :"min_pulse_width"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("2.422"); } @@ -305,7 +307,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type :"minimum_period"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("4.844"); } @@ -314,5 +316,6 @@ cell (sram_2_16_1_freepdk45){ } } } + } } diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm.lef b/compiler/tests/golden/sram_2_16_1_scn3me_subm.lef deleted file mode 100644 index 9d784677..00000000 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm.lef +++ /dev/null @@ -1,5533 +0,0 @@ -VERSION 5.4 ; -NAMESCASESENSITIVE ON ; -BUSBITCHARS "[]" ; -DIVIDERCHAR "/" ; -UNITS - DATABASE MICRONS 1000 ; -END UNITS -SITE MacroSite - CLASS Core ; - SIZE 148050.0 by 461850.0 ; -END MacroSite -MACRO sram_2_16_1_scn3me_subm - CLASS BLOCK ; - SIZE 148050.0 BY 461850.0 ; - SYMMETRY X Y R90 ; - SITE MacroSite ; - PIN DATA[0] - DIRECTION INOUT ; - PORT - LAYER metal2 ; - RECT 120900.0 0.0 121800.0 1800.0 ; - END - END DATA[0] - PIN DATA[1] - DIRECTION INOUT ; - PORT - LAYER metal2 ; - RECT 131100.0 0.0 132000.0 1800.0 ; - END - END DATA[1] - PIN ADDR[0] - DIRECTION INPUT ; - PORT - LAYER metal3 ; - RECT 0.0 87600.0 10800.0 89100.0 ; - END - END ADDR[0] - PIN ADDR[1] - DIRECTION INPUT ; - PORT - LAYER metal3 ; - RECT 0.0 77400.0 10800.0 78900.0 ; - END - END ADDR[1] - PIN ADDR[2] - DIRECTION INPUT ; - PORT - LAYER metal3 ; - RECT 0.0 67200.0 10800.0 68700.0 ; - END - END ADDR[2] - PIN ADDR[3] - DIRECTION INPUT ; - PORT - LAYER metal3 ; - RECT 0.0 57000.0 10800.0 58500.0 ; - END - END ADDR[3] - PIN CSb - DIRECTION INPUT ; - PORT - LAYER metal3 ; - RECT -38400.0 182700.0 -36600.0 184500.0 ; - END - END CSb - PIN WEb - DIRECTION INPUT ; - PORT - LAYER metal3 ; - RECT -28200.0 182700.0 -26400.0 184500.0 ; - END - END WEb - PIN OEb - DIRECTION INPUT ; - PORT - LAYER metal3 ; - RECT -48600.0 182700.0 -46800.0 184500.0 ; - END - END OEb - PIN clk - DIRECTION INPUT ; - PORT - LAYER metal1 ; - RECT -10200.0 181800.0 -9000.0 185400.0 ; - END - END clk - PIN vdd - DIRECTION INOUT ; - USE POWER ; - SHAPE ABUTMENT ; - PORT - LAYER metal2 ; - RECT 4950.0 0.0 8550.0 461850.0 ; - LAYER metal2 ; - RECT 144450.0 0.0 148050.0 461850.0 ; - LAYER metal1 ; - RECT 0.0 4950.0 148050.0 8550.0 ; - LAYER metal1 ; - RECT 0.0 458250.0 148050.0 461850.0 ; - END - END vdd - PIN gnd - DIRECTION INOUT ; - USE GROUND ; - SHAPE ABUTMENT ; - PORT - LAYER metal2 ; - RECT 0.0 0.0 3600.0 461850.0 ; - LAYER metal2 ; - RECT 139500.0 0.0 143100.0 461850.0 ; - LAYER metal1 ; - RECT 0.0 0.0 148050.0 3600.0 ; - LAYER metal1 ; - RECT 0.0 453300.0 148050.0 456900.0 ; - END - END gnd - OBS - LAYER metal1 ; - RECT 48300.0 215550.0 49200.0 216450.0 ; - RECT 48300.0 213150.0 49200.0 214050.0 ; - RECT 46950.0 215550.0 48750.0 216450.0 ; - RECT 48300.0 213600.0 49200.0 216000.0 ; - RECT 48750.0 213150.0 50700.0 214050.0 ; - RECT 100800.0 215550.0 101700.0 216450.0 ; - RECT 100800.0 211050.0 101700.0 211950.0 ; - RECT 86850.0 215550.0 101250.0 216450.0 ; - RECT 100800.0 211500.0 101700.0 216000.0 ; - RECT 101250.0 211050.0 115800.0 211950.0 ; - RECT 48300.0 229950.0 49200.0 230850.0 ; - RECT 48300.0 232350.0 49200.0 233250.0 ; - RECT 46950.0 229950.0 48750.0 230850.0 ; - RECT 48300.0 230400.0 49200.0 232800.0 ; - RECT 48750.0 232350.0 50700.0 233250.0 ; - RECT 100800.0 229950.0 101700.0 230850.0 ; - RECT 100800.0 234450.0 101700.0 235350.0 ; - RECT 86850.0 229950.0 101250.0 230850.0 ; - RECT 100800.0 230400.0 101700.0 234900.0 ; - RECT 101250.0 234450.0 115800.0 235350.0 ; - RECT 48300.0 243150.0 49200.0 244050.0 ; - RECT 48300.0 240750.0 49200.0 241650.0 ; - RECT 46950.0 243150.0 48750.0 244050.0 ; - RECT 48300.0 241200.0 49200.0 243600.0 ; - RECT 48750.0 240750.0 50700.0 241650.0 ; - RECT 100800.0 243150.0 101700.0 244050.0 ; - RECT 100800.0 238650.0 101700.0 239550.0 ; - RECT 86850.0 243150.0 101250.0 244050.0 ; - RECT 100800.0 239100.0 101700.0 243600.0 ; - RECT 101250.0 238650.0 115800.0 239550.0 ; - RECT 48300.0 257550.0 49200.0 258450.0 ; - RECT 48300.0 259950.0 49200.0 260850.0 ; - RECT 46950.0 257550.0 48750.0 258450.0 ; - RECT 48300.0 258000.0 49200.0 260400.0 ; - RECT 48750.0 259950.0 50700.0 260850.0 ; - RECT 100800.0 257550.0 101700.0 258450.0 ; - RECT 100800.0 262050.0 101700.0 262950.0 ; - RECT 86850.0 257550.0 101250.0 258450.0 ; - RECT 100800.0 258000.0 101700.0 262500.0 ; - RECT 101250.0 262050.0 115800.0 262950.0 ; - RECT 48300.0 270750.0 49200.0 271650.0 ; - RECT 48300.0 268350.0 49200.0 269250.0 ; - RECT 46950.0 270750.0 48750.0 271650.0 ; - RECT 48300.0 268800.0 49200.0 271200.0 ; - RECT 48750.0 268350.0 50700.0 269250.0 ; - RECT 100800.0 270750.0 101700.0 271650.0 ; - RECT 100800.0 266250.0 101700.0 267150.0 ; - RECT 86850.0 270750.0 101250.0 271650.0 ; - RECT 100800.0 266700.0 101700.0 271200.0 ; - RECT 101250.0 266250.0 115800.0 267150.0 ; - RECT 48300.0 285150.0 49200.0 286050.0 ; - RECT 48300.0 287550.0 49200.0 288450.0 ; - RECT 46950.0 285150.0 48750.0 286050.0 ; - RECT 48300.0 285600.0 49200.0 288000.0 ; - RECT 48750.0 287550.0 50700.0 288450.0 ; - RECT 100800.0 285150.0 101700.0 286050.0 ; - RECT 100800.0 289650.0 101700.0 290550.0 ; - RECT 86850.0 285150.0 101250.0 286050.0 ; - RECT 100800.0 285600.0 101700.0 290100.0 ; - RECT 101250.0 289650.0 115800.0 290550.0 ; - RECT 48300.0 298350.0 49200.0 299250.0 ; - RECT 48300.0 295950.0 49200.0 296850.0 ; - RECT 46950.0 298350.0 48750.0 299250.0 ; - RECT 48300.0 296400.0 49200.0 298800.0 ; - RECT 48750.0 295950.0 50700.0 296850.0 ; - RECT 100800.0 298350.0 101700.0 299250.0 ; - RECT 100800.0 293850.0 101700.0 294750.0 ; - RECT 86850.0 298350.0 101250.0 299250.0 ; - RECT 100800.0 294300.0 101700.0 298800.0 ; - RECT 101250.0 293850.0 115800.0 294750.0 ; - RECT 48300.0 312750.0 49200.0 313650.0 ; - RECT 48300.0 315150.0 49200.0 316050.0 ; - RECT 46950.0 312750.0 48750.0 313650.0 ; - RECT 48300.0 313200.0 49200.0 315600.0 ; - RECT 48750.0 315150.0 50700.0 316050.0 ; - RECT 100800.0 312750.0 101700.0 313650.0 ; - RECT 100800.0 317250.0 101700.0 318150.0 ; - RECT 86850.0 312750.0 101250.0 313650.0 ; - RECT 100800.0 313200.0 101700.0 317700.0 ; - RECT 101250.0 317250.0 115800.0 318150.0 ; - RECT 48300.0 325950.0 49200.0 326850.0 ; - RECT 48300.0 323550.0 49200.0 324450.0 ; - RECT 46950.0 325950.0 48750.0 326850.0 ; - RECT 48300.0 324000.0 49200.0 326400.0 ; - RECT 48750.0 323550.0 50700.0 324450.0 ; - RECT 100800.0 325950.0 101700.0 326850.0 ; - RECT 100800.0 321450.0 101700.0 322350.0 ; - RECT 86850.0 325950.0 101250.0 326850.0 ; - RECT 100800.0 321900.0 101700.0 326400.0 ; - RECT 101250.0 321450.0 115800.0 322350.0 ; - RECT 48300.0 340350.0 49200.0 341250.0 ; - RECT 48300.0 342750.0 49200.0 343650.0 ; - RECT 46950.0 340350.0 48750.0 341250.0 ; - RECT 48300.0 340800.0 49200.0 343200.0 ; - RECT 48750.0 342750.0 50700.0 343650.0 ; - RECT 100800.0 340350.0 101700.0 341250.0 ; - RECT 100800.0 344850.0 101700.0 345750.0 ; - RECT 86850.0 340350.0 101250.0 341250.0 ; - RECT 100800.0 340800.0 101700.0 345300.0 ; - RECT 101250.0 344850.0 115800.0 345750.0 ; - RECT 48300.0 353550.0 49200.0 354450.0 ; - RECT 48300.0 351150.0 49200.0 352050.0 ; - RECT 46950.0 353550.0 48750.0 354450.0 ; - RECT 48300.0 351600.0 49200.0 354000.0 ; - RECT 48750.0 351150.0 50700.0 352050.0 ; - RECT 100800.0 353550.0 101700.0 354450.0 ; - RECT 100800.0 349050.0 101700.0 349950.0 ; - RECT 86850.0 353550.0 101250.0 354450.0 ; - RECT 100800.0 349500.0 101700.0 354000.0 ; - RECT 101250.0 349050.0 115800.0 349950.0 ; - RECT 48300.0 367950.0 49200.0 368850.0 ; - RECT 48300.0 370350.0 49200.0 371250.0 ; - RECT 46950.0 367950.0 48750.0 368850.0 ; - RECT 48300.0 368400.0 49200.0 370800.0 ; - RECT 48750.0 370350.0 50700.0 371250.0 ; - RECT 100800.0 367950.0 101700.0 368850.0 ; - RECT 100800.0 372450.0 101700.0 373350.0 ; - RECT 86850.0 367950.0 101250.0 368850.0 ; - RECT 100800.0 368400.0 101700.0 372900.0 ; - RECT 101250.0 372450.0 115800.0 373350.0 ; - RECT 48300.0 381150.0 49200.0 382050.0 ; - RECT 48300.0 378750.0 49200.0 379650.0 ; - RECT 46950.0 381150.0 48750.0 382050.0 ; - RECT 48300.0 379200.0 49200.0 381600.0 ; - RECT 48750.0 378750.0 50700.0 379650.0 ; - RECT 100800.0 381150.0 101700.0 382050.0 ; - RECT 100800.0 376650.0 101700.0 377550.0 ; - RECT 86850.0 381150.0 101250.0 382050.0 ; - RECT 100800.0 377100.0 101700.0 381600.0 ; - RECT 101250.0 376650.0 115800.0 377550.0 ; - RECT 48300.0 395550.0 49200.0 396450.0 ; - RECT 48300.0 397950.0 49200.0 398850.0 ; - RECT 46950.0 395550.0 48750.0 396450.0 ; - RECT 48300.0 396000.0 49200.0 398400.0 ; - RECT 48750.0 397950.0 50700.0 398850.0 ; - RECT 100800.0 395550.0 101700.0 396450.0 ; - RECT 100800.0 400050.0 101700.0 400950.0 ; - RECT 86850.0 395550.0 101250.0 396450.0 ; - RECT 100800.0 396000.0 101700.0 400500.0 ; - RECT 101250.0 400050.0 115800.0 400950.0 ; - RECT 48300.0 408750.0 49200.0 409650.0 ; - RECT 48300.0 406350.0 49200.0 407250.0 ; - RECT 46950.0 408750.0 48750.0 409650.0 ; - RECT 48300.0 406800.0 49200.0 409200.0 ; - RECT 48750.0 406350.0 50700.0 407250.0 ; - RECT 100800.0 408750.0 101700.0 409650.0 ; - RECT 100800.0 404250.0 101700.0 405150.0 ; - RECT 86850.0 408750.0 101250.0 409650.0 ; - RECT 100800.0 404700.0 101700.0 409200.0 ; - RECT 101250.0 404250.0 115800.0 405150.0 ; - RECT 48300.0 423150.0 49200.0 424050.0 ; - RECT 48300.0 425550.0 49200.0 426450.0 ; - RECT 46950.0 423150.0 48750.0 424050.0 ; - RECT 48300.0 423600.0 49200.0 426000.0 ; - RECT 48750.0 425550.0 50700.0 426450.0 ; - RECT 100800.0 423150.0 101700.0 424050.0 ; - RECT 100800.0 427650.0 101700.0 428550.0 ; - RECT 86850.0 423150.0 101250.0 424050.0 ; - RECT 100800.0 423600.0 101700.0 428100.0 ; - RECT 101250.0 427650.0 115800.0 428550.0 ; - RECT 81300.0 101250.0 85800.0 102150.0 ; - RECT 78300.0 115050.0 88500.0 115950.0 ; - RECT 81300.0 156450.0 91200.0 157350.0 ; - RECT 78300.0 170250.0 93900.0 171150.0 ; - RECT 1800.0 98550.0 81300.0 99450.0 ; - RECT 1800.0 126150.0 81300.0 127050.0 ; - RECT 1800.0 153750.0 81300.0 154650.0 ; - RECT 1800.0 181350.0 81300.0 182250.0 ; - RECT 6750.0 112350.0 81300.0 113250.0 ; - RECT 6750.0 139950.0 81300.0 140850.0 ; - RECT 6750.0 167550.0 81300.0 168450.0 ; - RECT 6750.0 195150.0 81300.0 196050.0 ; - RECT 68700.0 87300.0 85800.0 88200.0 ; - RECT 68700.0 78600.0 88500.0 79500.0 ; - RECT 68700.0 66900.0 91200.0 67800.0 ; - RECT 68700.0 58200.0 93900.0 59100.0 ; - RECT 1800.0 82950.0 9900.0 83850.0 ; - RECT 1800.0 62550.0 9900.0 63450.0 ; - RECT 66300.0 50250.0 67200.0 51150.0 ; - RECT 66300.0 50700.0 67200.0 52800.0 ; - RECT 6750.0 50250.0 66750.0 51150.0 ; - RECT 104700.0 42300.0 116400.0 43200.0 ; - RECT 99300.0 37800.0 116400.0 38700.0 ; - RECT 102000.0 35400.0 116400.0 36300.0 ; - RECT 104700.0 438600.0 116400.0 439500.0 ; - RECT 107400.0 107100.0 116400.0 108000.0 ; - RECT 110100.0 205200.0 116400.0 206100.0 ; - RECT 12300.0 95250.0 13200.0 96150.0 ; - RECT 12300.0 93600.0 13200.0 95700.0 ; - RECT 12750.0 95250.0 96600.0 96150.0 ; - RECT 53850.0 431850.0 97500.0 432750.0 ; - RECT 116400.0 449700.0 146250.0 450600.0 ; - RECT 116400.0 177900.0 146250.0 178800.0 ; - RECT 116400.0 109200.0 146250.0 110100.0 ; - RECT 116400.0 96300.0 146250.0 97200.0 ; - RECT 116400.0 19500.0 146250.0 20400.0 ; - RECT 6750.0 222750.0 146250.0 223650.0 ; - RECT 6750.0 250350.0 146250.0 251250.0 ; - RECT 6750.0 277950.0 146250.0 278850.0 ; - RECT 6750.0 305550.0 146250.0 306450.0 ; - RECT 6750.0 333150.0 146250.0 334050.0 ; - RECT 6750.0 360750.0 146250.0 361650.0 ; - RECT 6750.0 388350.0 146250.0 389250.0 ; - RECT 6750.0 415950.0 146250.0 416850.0 ; - RECT 116400.0 33300.0 143100.0 34200.0 ; - RECT 116400.0 203100.0 143100.0 204000.0 ; - RECT 116400.0 105000.0 143100.0 105900.0 ; - RECT 1800.0 208950.0 57000.0 209850.0 ; - RECT 1800.0 236550.0 57000.0 237450.0 ; - RECT 1800.0 264150.0 57000.0 265050.0 ; - RECT 1800.0 291750.0 57000.0 292650.0 ; - RECT 1800.0 319350.0 57000.0 320250.0 ; - RECT 1800.0 346950.0 57000.0 347850.0 ; - RECT 1800.0 374550.0 57000.0 375450.0 ; - RECT 1800.0 402150.0 57000.0 403050.0 ; - RECT 1800.0 429750.0 57000.0 430650.0 ; - RECT 116400.0 209400.0 126600.0 223200.0 ; - RECT 116400.0 237000.0 126600.0 223200.0 ; - RECT 116400.0 237000.0 126600.0 250800.0 ; - RECT 116400.0 264600.0 126600.0 250800.0 ; - RECT 116400.0 264600.0 126600.0 278400.0 ; - RECT 116400.0 292200.0 126600.0 278400.0 ; - RECT 116400.0 292200.0 126600.0 306000.0 ; - RECT 116400.0 319800.0 126600.0 306000.0 ; - RECT 116400.0 319800.0 126600.0 333600.0 ; - RECT 116400.0 347400.0 126600.0 333600.0 ; - RECT 116400.0 347400.0 126600.0 361200.0 ; - RECT 116400.0 375000.0 126600.0 361200.0 ; - RECT 116400.0 375000.0 126600.0 388800.0 ; - RECT 116400.0 402600.0 126600.0 388800.0 ; - RECT 116400.0 402600.0 126600.0 416400.0 ; - RECT 116400.0 430200.0 126600.0 416400.0 ; - RECT 126600.0 209400.0 136800.0 223200.0 ; - RECT 126600.0 237000.0 136800.0 223200.0 ; - RECT 126600.0 237000.0 136800.0 250800.0 ; - RECT 126600.0 264600.0 136800.0 250800.0 ; - RECT 126600.0 264600.0 136800.0 278400.0 ; - RECT 126600.0 292200.0 136800.0 278400.0 ; - RECT 126600.0 292200.0 136800.0 306000.0 ; - RECT 126600.0 319800.0 136800.0 306000.0 ; - RECT 126600.0 319800.0 136800.0 333600.0 ; - RECT 126600.0 347400.0 136800.0 333600.0 ; - RECT 126600.0 347400.0 136800.0 361200.0 ; - RECT 126600.0 375000.0 136800.0 361200.0 ; - RECT 126600.0 375000.0 136800.0 388800.0 ; - RECT 126600.0 402600.0 136800.0 388800.0 ; - RECT 126600.0 402600.0 136800.0 416400.0 ; - RECT 126600.0 430200.0 136800.0 416400.0 ; - RECT 115800.0 210900.0 137400.0 212100.0 ; - RECT 115800.0 234300.0 137400.0 235500.0 ; - RECT 115800.0 238500.0 137400.0 239700.0 ; - RECT 115800.0 261900.0 137400.0 263100.0 ; - RECT 115800.0 266100.0 137400.0 267300.0 ; - RECT 115800.0 289500.0 137400.0 290700.0 ; - RECT 115800.0 293700.0 137400.0 294900.0 ; - RECT 115800.0 317100.0 137400.0 318300.0 ; - RECT 115800.0 321300.0 137400.0 322500.0 ; - RECT 115800.0 344700.0 137400.0 345900.0 ; - RECT 115800.0 348900.0 137400.0 350100.0 ; - RECT 115800.0 372300.0 137400.0 373500.0 ; - RECT 115800.0 376500.0 137400.0 377700.0 ; - RECT 115800.0 399900.0 137400.0 401100.0 ; - RECT 115800.0 404100.0 137400.0 405300.0 ; - RECT 115800.0 427500.0 137400.0 428700.0 ; - RECT 115800.0 222600.0 137400.0 223500.0 ; - RECT 115800.0 250200.0 137400.0 251100.0 ; - RECT 115800.0 277800.0 137400.0 278700.0 ; - RECT 115800.0 305400.0 137400.0 306300.0 ; - RECT 115800.0 333000.0 137400.0 333900.0 ; - RECT 115800.0 360600.0 137400.0 361500.0 ; - RECT 115800.0 388200.0 137400.0 389100.0 ; - RECT 115800.0 415800.0 137400.0 416700.0 ; - RECT 121800.0 443400.0 123000.0 450600.0 ; - RECT 119400.0 436200.0 120600.0 437400.0 ; - RECT 121800.0 436200.0 123000.0 437400.0 ; - RECT 121800.0 436200.0 123000.0 437400.0 ; - RECT 119400.0 436200.0 120600.0 437400.0 ; - RECT 119400.0 443400.0 120600.0 444600.0 ; - RECT 121800.0 443400.0 123000.0 444600.0 ; - RECT 121800.0 443400.0 123000.0 444600.0 ; - RECT 119400.0 443400.0 120600.0 444600.0 ; - RECT 121800.0 443400.0 123000.0 444600.0 ; - RECT 124200.0 443400.0 125400.0 444600.0 ; - RECT 124200.0 443400.0 125400.0 444600.0 ; - RECT 121800.0 443400.0 123000.0 444600.0 ; - RECT 121500.0 438450.0 120300.0 439650.0 ; - RECT 121800.0 448800.0 123000.0 450000.0 ; - RECT 119400.0 436200.0 120600.0 437400.0 ; - RECT 121800.0 436200.0 123000.0 437400.0 ; - RECT 119400.0 443400.0 120600.0 444600.0 ; - RECT 124200.0 443400.0 125400.0 444600.0 ; - RECT 116400.0 438600.0 126600.0 439500.0 ; - RECT 116400.0 449700.0 126600.0 450600.0 ; - RECT 132000.0 443400.0 133200.0 450600.0 ; - RECT 129600.0 436200.0 130800.0 437400.0 ; - RECT 132000.0 436200.0 133200.0 437400.0 ; - RECT 132000.0 436200.0 133200.0 437400.0 ; - RECT 129600.0 436200.0 130800.0 437400.0 ; - RECT 129600.0 443400.0 130800.0 444600.0 ; - RECT 132000.0 443400.0 133200.0 444600.0 ; - RECT 132000.0 443400.0 133200.0 444600.0 ; - RECT 129600.0 443400.0 130800.0 444600.0 ; - RECT 132000.0 443400.0 133200.0 444600.0 ; - RECT 134400.0 443400.0 135600.0 444600.0 ; - RECT 134400.0 443400.0 135600.0 444600.0 ; - RECT 132000.0 443400.0 133200.0 444600.0 ; - RECT 131700.0 438450.0 130500.0 439650.0 ; - RECT 132000.0 448800.0 133200.0 450000.0 ; - RECT 129600.0 436200.0 130800.0 437400.0 ; - RECT 132000.0 436200.0 133200.0 437400.0 ; - RECT 129600.0 443400.0 130800.0 444600.0 ; - RECT 134400.0 443400.0 135600.0 444600.0 ; - RECT 126600.0 438600.0 136800.0 439500.0 ; - RECT 126600.0 449700.0 136800.0 450600.0 ; - RECT 116400.0 438600.0 136800.0 439500.0 ; - RECT 116400.0 449700.0 136800.0 450600.0 ; - RECT 116400.0 160500.0 126600.0 209400.0 ; - RECT 126600.0 160500.0 136800.0 209400.0 ; - RECT 116400.0 205200.0 136800.0 206100.0 ; - RECT 116400.0 177900.0 136800.0 178800.0 ; - RECT 116400.0 203100.0 136800.0 204000.0 ; - RECT 116400.0 99900.0 126600.0 160500.0 ; - RECT 126600.0 99900.0 136800.0 160500.0 ; - RECT 116400.0 107100.0 136800.0 108000.0 ; - RECT 116400.0 109200.0 136800.0 110100.0 ; - RECT 116400.0 105000.0 136800.0 105900.0 ; - RECT 116400.0 39900.0 126600.0 99900.0 ; - RECT 136800.0 39900.0 126600.0 99900.0 ; - RECT 116400.0 42300.0 136800.0 43200.0 ; - RECT 116400.0 96300.0 136800.0 97200.0 ; - RECT 116400.0 39900.0 126600.0 18000.0 ; - RECT 126600.0 39900.0 136800.0 18000.0 ; - RECT 116400.0 36300.0 136800.0 35400.0 ; - RECT 116400.0 38700.0 136800.0 37800.0 ; - RECT 116400.0 20400.0 136800.0 19500.0 ; - RECT 116400.0 34200.0 136800.0 33300.0 ; - RECT 38550.0 216750.0 39450.0 217650.0 ; - RECT 38550.0 215550.0 39450.0 216450.0 ; - RECT 34500.0 216750.0 39000.0 217650.0 ; - RECT 38550.0 216000.0 39450.0 217200.0 ; - RECT 39000.0 215550.0 43500.0 216450.0 ; - RECT 38550.0 228750.0 39450.0 229650.0 ; - RECT 38550.0 229950.0 39450.0 230850.0 ; - RECT 34500.0 228750.0 39000.0 229650.0 ; - RECT 38550.0 229200.0 39450.0 230400.0 ; - RECT 39000.0 229950.0 43500.0 230850.0 ; - RECT 38550.0 244350.0 39450.0 245250.0 ; - RECT 38550.0 243150.0 39450.0 244050.0 ; - RECT 34500.0 244350.0 39000.0 245250.0 ; - RECT 38550.0 243600.0 39450.0 244800.0 ; - RECT 39000.0 243150.0 43500.0 244050.0 ; - RECT 38550.0 256350.0 39450.0 257250.0 ; - RECT 38550.0 257550.0 39450.0 258450.0 ; - RECT 34500.0 256350.0 39000.0 257250.0 ; - RECT 38550.0 256800.0 39450.0 258000.0 ; - RECT 39000.0 257550.0 43500.0 258450.0 ; - RECT 38550.0 271950.0 39450.0 272850.0 ; - RECT 38550.0 270750.0 39450.0 271650.0 ; - RECT 34500.0 271950.0 39000.0 272850.0 ; - RECT 38550.0 271200.0 39450.0 272400.0 ; - RECT 39000.0 270750.0 43500.0 271650.0 ; - RECT 38550.0 283950.0 39450.0 284850.0 ; - RECT 38550.0 285150.0 39450.0 286050.0 ; - RECT 34500.0 283950.0 39000.0 284850.0 ; - RECT 38550.0 284400.0 39450.0 285600.0 ; - RECT 39000.0 285150.0 43500.0 286050.0 ; - RECT 38550.0 299550.0 39450.0 300450.0 ; - RECT 38550.0 298350.0 39450.0 299250.0 ; - RECT 34500.0 299550.0 39000.0 300450.0 ; - RECT 38550.0 298800.0 39450.0 300000.0 ; - RECT 39000.0 298350.0 43500.0 299250.0 ; - RECT 38550.0 311550.0 39450.0 312450.0 ; - RECT 38550.0 312750.0 39450.0 313650.0 ; - RECT 34500.0 311550.0 39000.0 312450.0 ; - RECT 38550.0 312000.0 39450.0 313200.0 ; - RECT 39000.0 312750.0 43500.0 313650.0 ; - RECT 38550.0 327150.0 39450.0 328050.0 ; - RECT 38550.0 325950.0 39450.0 326850.0 ; - RECT 34500.0 327150.0 39000.0 328050.0 ; - RECT 38550.0 326400.0 39450.0 327600.0 ; - RECT 39000.0 325950.0 43500.0 326850.0 ; - RECT 38550.0 339150.0 39450.0 340050.0 ; - RECT 38550.0 340350.0 39450.0 341250.0 ; - RECT 34500.0 339150.0 39000.0 340050.0 ; - RECT 38550.0 339600.0 39450.0 340800.0 ; - RECT 39000.0 340350.0 43500.0 341250.0 ; - RECT 38550.0 354750.0 39450.0 355650.0 ; - RECT 38550.0 353550.0 39450.0 354450.0 ; - RECT 34500.0 354750.0 39000.0 355650.0 ; - RECT 38550.0 354000.0 39450.0 355200.0 ; - RECT 39000.0 353550.0 43500.0 354450.0 ; - RECT 38550.0 366750.0 39450.0 367650.0 ; - RECT 38550.0 367950.0 39450.0 368850.0 ; - RECT 34500.0 366750.0 39000.0 367650.0 ; - RECT 38550.0 367200.0 39450.0 368400.0 ; - RECT 39000.0 367950.0 43500.0 368850.0 ; - RECT 38550.0 382350.0 39450.0 383250.0 ; - RECT 38550.0 381150.0 39450.0 382050.0 ; - RECT 34500.0 382350.0 39000.0 383250.0 ; - RECT 38550.0 381600.0 39450.0 382800.0 ; - RECT 39000.0 381150.0 43500.0 382050.0 ; - RECT 38550.0 394350.0 39450.0 395250.0 ; - RECT 38550.0 395550.0 39450.0 396450.0 ; - RECT 34500.0 394350.0 39000.0 395250.0 ; - RECT 38550.0 394800.0 39450.0 396000.0 ; - RECT 39000.0 395550.0 43500.0 396450.0 ; - RECT 38550.0 409950.0 39450.0 410850.0 ; - RECT 38550.0 408750.0 39450.0 409650.0 ; - RECT 34500.0 409950.0 39000.0 410850.0 ; - RECT 38550.0 409200.0 39450.0 410400.0 ; - RECT 39000.0 408750.0 43500.0 409650.0 ; - RECT 38550.0 421950.0 39450.0 422850.0 ; - RECT 38550.0 423150.0 39450.0 424050.0 ; - RECT 34500.0 421950.0 39000.0 422850.0 ; - RECT 38550.0 422400.0 39450.0 423600.0 ; - RECT 39000.0 423150.0 43500.0 424050.0 ; - RECT 10350.0 105150.0 26700.0 106050.0 ; - RECT 12450.0 119550.0 26700.0 120450.0 ; - RECT 14550.0 132750.0 26700.0 133650.0 ; - RECT 16650.0 147150.0 26700.0 148050.0 ; - RECT 18750.0 160350.0 26700.0 161250.0 ; - RECT 20850.0 174750.0 26700.0 175650.0 ; - RECT 22950.0 187950.0 26700.0 188850.0 ; - RECT 25050.0 202350.0 26700.0 203250.0 ; - RECT 10350.0 216750.0 29100.0 217650.0 ; - RECT 18750.0 214050.0 32100.0 214950.0 ; - RECT 10350.0 228750.0 29100.0 229650.0 ; - RECT 20850.0 231450.0 32100.0 232350.0 ; - RECT 10350.0 244350.0 29100.0 245250.0 ; - RECT 22950.0 241650.0 32100.0 242550.0 ; - RECT 10350.0 256350.0 29100.0 257250.0 ; - RECT 25050.0 259050.0 32100.0 259950.0 ; - RECT 12450.0 271950.0 29100.0 272850.0 ; - RECT 18750.0 269250.0 32100.0 270150.0 ; - RECT 12450.0 283950.0 29100.0 284850.0 ; - RECT 20850.0 286650.0 32100.0 287550.0 ; - RECT 12450.0 299550.0 29100.0 300450.0 ; - RECT 22950.0 296850.0 32100.0 297750.0 ; - RECT 12450.0 311550.0 29100.0 312450.0 ; - RECT 25050.0 314250.0 32100.0 315150.0 ; - RECT 14550.0 327150.0 29100.0 328050.0 ; - RECT 18750.0 324450.0 32100.0 325350.0 ; - RECT 14550.0 339150.0 29100.0 340050.0 ; - RECT 20850.0 341850.0 32100.0 342750.0 ; - RECT 14550.0 354750.0 29100.0 355650.0 ; - RECT 22950.0 352050.0 32100.0 352950.0 ; - RECT 14550.0 366750.0 29100.0 367650.0 ; - RECT 25050.0 369450.0 32100.0 370350.0 ; - RECT 16650.0 382350.0 29100.0 383250.0 ; - RECT 18750.0 379650.0 32100.0 380550.0 ; - RECT 16650.0 394350.0 29100.0 395250.0 ; - RECT 20850.0 397050.0 32100.0 397950.0 ; - RECT 16650.0 409950.0 29100.0 410850.0 ; - RECT 22950.0 407250.0 32100.0 408150.0 ; - RECT 16650.0 421950.0 29100.0 422850.0 ; - RECT 25050.0 424650.0 32100.0 425550.0 ; - RECT 65250.0 105150.0 64350.0 106050.0 ; - RECT 65250.0 109650.0 64350.0 110550.0 ; - RECT 69450.0 105150.0 64800.0 106050.0 ; - RECT 65250.0 105600.0 64350.0 110100.0 ; - RECT 64800.0 109650.0 62250.0 110550.0 ; - RECT 80850.0 105150.0 72900.0 106050.0 ; - RECT 65250.0 119550.0 64350.0 120450.0 ; - RECT 65250.0 123450.0 64350.0 124350.0 ; - RECT 69450.0 119550.0 64800.0 120450.0 ; - RECT 65250.0 120000.0 64350.0 123900.0 ; - RECT 64800.0 123450.0 59250.0 124350.0 ; - RECT 77850.0 119550.0 72900.0 120450.0 ; - RECT 80850.0 128250.0 56250.0 129150.0 ; - RECT 77850.0 142050.0 53250.0 142950.0 ; - RECT 62250.0 106350.0 48300.0 107250.0 ; - RECT 59250.0 103650.0 45300.0 104550.0 ; - RECT 56250.0 118350.0 48300.0 119250.0 ; - RECT 59250.0 121050.0 45300.0 121950.0 ; - RECT 62250.0 133950.0 48300.0 134850.0 ; - RECT 53250.0 131250.0 45300.0 132150.0 ; - RECT 56250.0 145950.0 48300.0 146850.0 ; - RECT 53250.0 148650.0 45300.0 149550.0 ; - RECT 38850.0 106350.0 37950.0 107250.0 ; - RECT 38850.0 105150.0 37950.0 106050.0 ; - RECT 42900.0 106350.0 38400.0 107250.0 ; - RECT 38850.0 105600.0 37950.0 106800.0 ; - RECT 38400.0 105150.0 33900.0 106050.0 ; - RECT 38850.0 118350.0 37950.0 119250.0 ; - RECT 38850.0 119550.0 37950.0 120450.0 ; - RECT 42900.0 118350.0 38400.0 119250.0 ; - RECT 38850.0 118800.0 37950.0 120000.0 ; - RECT 38400.0 119550.0 33900.0 120450.0 ; - RECT 38850.0 133950.0 37950.0 134850.0 ; - RECT 38850.0 132750.0 37950.0 133650.0 ; - RECT 42900.0 133950.0 38400.0 134850.0 ; - RECT 38850.0 133200.0 37950.0 134400.0 ; - RECT 38400.0 132750.0 33900.0 133650.0 ; - RECT 38850.0 145950.0 37950.0 146850.0 ; - RECT 38850.0 147150.0 37950.0 148050.0 ; - RECT 42900.0 145950.0 38400.0 146850.0 ; - RECT 38850.0 146400.0 37950.0 147600.0 ; - RECT 38400.0 147150.0 33900.0 148050.0 ; - RECT 68700.0 110850.0 67500.0 112800.0 ; - RECT 68700.0 99000.0 67500.0 100950.0 ; - RECT 73500.0 100350.0 72300.0 98550.0 ; - RECT 73500.0 109650.0 72300.0 113250.0 ; - RECT 70800.0 100350.0 69900.0 109650.0 ; - RECT 73500.0 109650.0 72300.0 110850.0 ; - RECT 71100.0 109650.0 69900.0 110850.0 ; - RECT 71100.0 109650.0 69900.0 110850.0 ; - RECT 73500.0 109650.0 72300.0 110850.0 ; - RECT 73500.0 100350.0 72300.0 101550.0 ; - RECT 71100.0 100350.0 69900.0 101550.0 ; - RECT 71100.0 100350.0 69900.0 101550.0 ; - RECT 73500.0 100350.0 72300.0 101550.0 ; - RECT 68700.0 110250.0 67500.0 111450.0 ; - RECT 68700.0 100350.0 67500.0 101550.0 ; - RECT 72900.0 105000.0 71700.0 106200.0 ; - RECT 72900.0 105000.0 71700.0 106200.0 ; - RECT 70350.0 105150.0 69450.0 106050.0 ; - RECT 75300.0 112350.0 65700.0 113250.0 ; - RECT 75300.0 98550.0 65700.0 99450.0 ; - RECT 68700.0 114750.0 67500.0 112800.0 ; - RECT 68700.0 126600.0 67500.0 124650.0 ; - RECT 73500.0 125250.0 72300.0 127050.0 ; - RECT 73500.0 115950.0 72300.0 112350.0 ; - RECT 70800.0 125250.0 69900.0 115950.0 ; - RECT 73500.0 115950.0 72300.0 114750.0 ; - RECT 71100.0 115950.0 69900.0 114750.0 ; - RECT 71100.0 115950.0 69900.0 114750.0 ; - RECT 73500.0 115950.0 72300.0 114750.0 ; - RECT 73500.0 125250.0 72300.0 124050.0 ; - RECT 71100.0 125250.0 69900.0 124050.0 ; - RECT 71100.0 125250.0 69900.0 124050.0 ; - RECT 73500.0 125250.0 72300.0 124050.0 ; - RECT 68700.0 115350.0 67500.0 114150.0 ; - RECT 68700.0 125250.0 67500.0 124050.0 ; - RECT 72900.0 120600.0 71700.0 119400.0 ; - RECT 72900.0 120600.0 71700.0 119400.0 ; - RECT 70350.0 120450.0 69450.0 119550.0 ; - RECT 75300.0 113250.0 65700.0 112350.0 ; - RECT 75300.0 127050.0 65700.0 126150.0 ; - RECT 29700.0 110850.0 28500.0 112800.0 ; - RECT 29700.0 99000.0 28500.0 100950.0 ; - RECT 34500.0 100350.0 33300.0 98550.0 ; - RECT 34500.0 109650.0 33300.0 113250.0 ; - RECT 31800.0 100350.0 30900.0 109650.0 ; - RECT 34500.0 109650.0 33300.0 110850.0 ; - RECT 32100.0 109650.0 30900.0 110850.0 ; - RECT 32100.0 109650.0 30900.0 110850.0 ; - RECT 34500.0 109650.0 33300.0 110850.0 ; - RECT 34500.0 100350.0 33300.0 101550.0 ; - RECT 32100.0 100350.0 30900.0 101550.0 ; - RECT 32100.0 100350.0 30900.0 101550.0 ; - RECT 34500.0 100350.0 33300.0 101550.0 ; - RECT 29700.0 110250.0 28500.0 111450.0 ; - RECT 29700.0 100350.0 28500.0 101550.0 ; - RECT 33900.0 105000.0 32700.0 106200.0 ; - RECT 33900.0 105000.0 32700.0 106200.0 ; - RECT 31350.0 105150.0 30450.0 106050.0 ; - RECT 36300.0 112350.0 26700.0 113250.0 ; - RECT 36300.0 98550.0 26700.0 99450.0 ; - RECT 29700.0 114750.0 28500.0 112800.0 ; - RECT 29700.0 126600.0 28500.0 124650.0 ; - RECT 34500.0 125250.0 33300.0 127050.0 ; - RECT 34500.0 115950.0 33300.0 112350.0 ; - RECT 31800.0 125250.0 30900.0 115950.0 ; - RECT 34500.0 115950.0 33300.0 114750.0 ; - RECT 32100.0 115950.0 30900.0 114750.0 ; - RECT 32100.0 115950.0 30900.0 114750.0 ; - RECT 34500.0 115950.0 33300.0 114750.0 ; - RECT 34500.0 125250.0 33300.0 124050.0 ; - RECT 32100.0 125250.0 30900.0 124050.0 ; - RECT 32100.0 125250.0 30900.0 124050.0 ; - RECT 34500.0 125250.0 33300.0 124050.0 ; - RECT 29700.0 115350.0 28500.0 114150.0 ; - RECT 29700.0 125250.0 28500.0 124050.0 ; - RECT 33900.0 120600.0 32700.0 119400.0 ; - RECT 33900.0 120600.0 32700.0 119400.0 ; - RECT 31350.0 120450.0 30450.0 119550.0 ; - RECT 36300.0 113250.0 26700.0 112350.0 ; - RECT 36300.0 127050.0 26700.0 126150.0 ; - RECT 29700.0 138450.0 28500.0 140400.0 ; - RECT 29700.0 126600.0 28500.0 128550.0 ; - RECT 34500.0 127950.0 33300.0 126150.0 ; - RECT 34500.0 137250.0 33300.0 140850.0 ; - RECT 31800.0 127950.0 30900.0 137250.0 ; - RECT 34500.0 137250.0 33300.0 138450.0 ; - RECT 32100.0 137250.0 30900.0 138450.0 ; - RECT 32100.0 137250.0 30900.0 138450.0 ; - RECT 34500.0 137250.0 33300.0 138450.0 ; - RECT 34500.0 127950.0 33300.0 129150.0 ; - RECT 32100.0 127950.0 30900.0 129150.0 ; - RECT 32100.0 127950.0 30900.0 129150.0 ; - RECT 34500.0 127950.0 33300.0 129150.0 ; - RECT 29700.0 137850.0 28500.0 139050.0 ; - RECT 29700.0 127950.0 28500.0 129150.0 ; - RECT 33900.0 132600.0 32700.0 133800.0 ; - RECT 33900.0 132600.0 32700.0 133800.0 ; - RECT 31350.0 132750.0 30450.0 133650.0 ; - RECT 36300.0 139950.0 26700.0 140850.0 ; - RECT 36300.0 126150.0 26700.0 127050.0 ; - RECT 29700.0 142350.0 28500.0 140400.0 ; - RECT 29700.0 154200.0 28500.0 152250.0 ; - RECT 34500.0 152850.0 33300.0 154650.0 ; - RECT 34500.0 143550.0 33300.0 139950.0 ; - RECT 31800.0 152850.0 30900.0 143550.0 ; - RECT 34500.0 143550.0 33300.0 142350.0 ; - RECT 32100.0 143550.0 30900.0 142350.0 ; - RECT 32100.0 143550.0 30900.0 142350.0 ; - RECT 34500.0 143550.0 33300.0 142350.0 ; - RECT 34500.0 152850.0 33300.0 151650.0 ; - RECT 32100.0 152850.0 30900.0 151650.0 ; - RECT 32100.0 152850.0 30900.0 151650.0 ; - RECT 34500.0 152850.0 33300.0 151650.0 ; - RECT 29700.0 142950.0 28500.0 141750.0 ; - RECT 29700.0 152850.0 28500.0 151650.0 ; - RECT 33900.0 148200.0 32700.0 147000.0 ; - RECT 33900.0 148200.0 32700.0 147000.0 ; - RECT 31350.0 148050.0 30450.0 147150.0 ; - RECT 36300.0 140850.0 26700.0 139950.0 ; - RECT 36300.0 154650.0 26700.0 153750.0 ; - RECT 48900.0 100950.0 47700.0 98550.0 ; - RECT 48900.0 109650.0 47700.0 113250.0 ; - RECT 44100.0 109650.0 42900.0 113250.0 ; - RECT 41700.0 110850.0 40500.0 112800.0 ; - RECT 41700.0 99000.0 40500.0 100950.0 ; - RECT 48900.0 109650.0 47700.0 110850.0 ; - RECT 46500.0 109650.0 45300.0 110850.0 ; - RECT 46500.0 109650.0 45300.0 110850.0 ; - RECT 48900.0 109650.0 47700.0 110850.0 ; - RECT 46500.0 109650.0 45300.0 110850.0 ; - RECT 44100.0 109650.0 42900.0 110850.0 ; - RECT 44100.0 109650.0 42900.0 110850.0 ; - RECT 46500.0 109650.0 45300.0 110850.0 ; - RECT 48900.0 100950.0 47700.0 102150.0 ; - RECT 46500.0 100950.0 45300.0 102150.0 ; - RECT 46500.0 100950.0 45300.0 102150.0 ; - RECT 48900.0 100950.0 47700.0 102150.0 ; - RECT 46500.0 100950.0 45300.0 102150.0 ; - RECT 44100.0 100950.0 42900.0 102150.0 ; - RECT 44100.0 100950.0 42900.0 102150.0 ; - RECT 46500.0 100950.0 45300.0 102150.0 ; - RECT 41700.0 110250.0 40500.0 111450.0 ; - RECT 41700.0 100350.0 40500.0 101550.0 ; - RECT 44100.0 103500.0 45300.0 104700.0 ; - RECT 47100.0 106200.0 48300.0 107400.0 ; - RECT 46500.0 109650.0 45300.0 110850.0 ; - RECT 44100.0 100950.0 42900.0 102150.0 ; - RECT 42900.0 106200.0 44100.0 107400.0 ; - RECT 48300.0 106200.0 47100.0 107400.0 ; - RECT 45300.0 103500.0 44100.0 104700.0 ; - RECT 44100.0 106200.0 42900.0 107400.0 ; - RECT 50700.0 112350.0 36300.0 113250.0 ; - RECT 50700.0 98550.0 36300.0 99450.0 ; - RECT 48900.0 124650.0 47700.0 127050.0 ; - RECT 48900.0 115950.0 47700.0 112350.0 ; - RECT 44100.0 115950.0 42900.0 112350.0 ; - RECT 41700.0 114750.0 40500.0 112800.0 ; - RECT 41700.0 126600.0 40500.0 124650.0 ; - RECT 48900.0 115950.0 47700.0 114750.0 ; - RECT 46500.0 115950.0 45300.0 114750.0 ; - RECT 46500.0 115950.0 45300.0 114750.0 ; - RECT 48900.0 115950.0 47700.0 114750.0 ; - RECT 46500.0 115950.0 45300.0 114750.0 ; - RECT 44100.0 115950.0 42900.0 114750.0 ; - RECT 44100.0 115950.0 42900.0 114750.0 ; - RECT 46500.0 115950.0 45300.0 114750.0 ; - RECT 48900.0 124650.0 47700.0 123450.0 ; - RECT 46500.0 124650.0 45300.0 123450.0 ; - RECT 46500.0 124650.0 45300.0 123450.0 ; - RECT 48900.0 124650.0 47700.0 123450.0 ; - RECT 46500.0 124650.0 45300.0 123450.0 ; - RECT 44100.0 124650.0 42900.0 123450.0 ; - RECT 44100.0 124650.0 42900.0 123450.0 ; - RECT 46500.0 124650.0 45300.0 123450.0 ; - RECT 41700.0 115350.0 40500.0 114150.0 ; - RECT 41700.0 125250.0 40500.0 124050.0 ; - RECT 44100.0 122100.0 45300.0 120900.0 ; - RECT 47100.0 119400.0 48300.0 118200.0 ; - RECT 46500.0 115950.0 45300.0 114750.0 ; - RECT 44100.0 124650.0 42900.0 123450.0 ; - RECT 42900.0 119400.0 44100.0 118200.0 ; - RECT 48300.0 119400.0 47100.0 118200.0 ; - RECT 45300.0 122100.0 44100.0 120900.0 ; - RECT 44100.0 119400.0 42900.0 118200.0 ; - RECT 50700.0 113250.0 36300.0 112350.0 ; - RECT 50700.0 127050.0 36300.0 126150.0 ; - RECT 48900.0 128550.0 47700.0 126150.0 ; - RECT 48900.0 137250.0 47700.0 140850.0 ; - RECT 44100.0 137250.0 42900.0 140850.0 ; - RECT 41700.0 138450.0 40500.0 140400.0 ; - RECT 41700.0 126600.0 40500.0 128550.0 ; - RECT 48900.0 137250.0 47700.0 138450.0 ; - RECT 46500.0 137250.0 45300.0 138450.0 ; - RECT 46500.0 137250.0 45300.0 138450.0 ; - RECT 48900.0 137250.0 47700.0 138450.0 ; - RECT 46500.0 137250.0 45300.0 138450.0 ; - RECT 44100.0 137250.0 42900.0 138450.0 ; - RECT 44100.0 137250.0 42900.0 138450.0 ; - RECT 46500.0 137250.0 45300.0 138450.0 ; - RECT 48900.0 128550.0 47700.0 129750.0 ; - RECT 46500.0 128550.0 45300.0 129750.0 ; - RECT 46500.0 128550.0 45300.0 129750.0 ; - RECT 48900.0 128550.0 47700.0 129750.0 ; - RECT 46500.0 128550.0 45300.0 129750.0 ; - RECT 44100.0 128550.0 42900.0 129750.0 ; - RECT 44100.0 128550.0 42900.0 129750.0 ; - RECT 46500.0 128550.0 45300.0 129750.0 ; - RECT 41700.0 137850.0 40500.0 139050.0 ; - RECT 41700.0 127950.0 40500.0 129150.0 ; - RECT 44100.0 131100.0 45300.0 132300.0 ; - RECT 47100.0 133800.0 48300.0 135000.0 ; - RECT 46500.0 137250.0 45300.0 138450.0 ; - RECT 44100.0 128550.0 42900.0 129750.0 ; - RECT 42900.0 133800.0 44100.0 135000.0 ; - RECT 48300.0 133800.0 47100.0 135000.0 ; - RECT 45300.0 131100.0 44100.0 132300.0 ; - RECT 44100.0 133800.0 42900.0 135000.0 ; - RECT 50700.0 139950.0 36300.0 140850.0 ; - RECT 50700.0 126150.0 36300.0 127050.0 ; - RECT 48900.0 152250.0 47700.0 154650.0 ; - RECT 48900.0 143550.0 47700.0 139950.0 ; - RECT 44100.0 143550.0 42900.0 139950.0 ; - RECT 41700.0 142350.0 40500.0 140400.0 ; - RECT 41700.0 154200.0 40500.0 152250.0 ; - RECT 48900.0 143550.0 47700.0 142350.0 ; - RECT 46500.0 143550.0 45300.0 142350.0 ; - RECT 46500.0 143550.0 45300.0 142350.0 ; - RECT 48900.0 143550.0 47700.0 142350.0 ; - RECT 46500.0 143550.0 45300.0 142350.0 ; - RECT 44100.0 143550.0 42900.0 142350.0 ; - RECT 44100.0 143550.0 42900.0 142350.0 ; - RECT 46500.0 143550.0 45300.0 142350.0 ; - RECT 48900.0 152250.0 47700.0 151050.0 ; - RECT 46500.0 152250.0 45300.0 151050.0 ; - RECT 46500.0 152250.0 45300.0 151050.0 ; - RECT 48900.0 152250.0 47700.0 151050.0 ; - RECT 46500.0 152250.0 45300.0 151050.0 ; - RECT 44100.0 152250.0 42900.0 151050.0 ; - RECT 44100.0 152250.0 42900.0 151050.0 ; - RECT 46500.0 152250.0 45300.0 151050.0 ; - RECT 41700.0 142950.0 40500.0 141750.0 ; - RECT 41700.0 152850.0 40500.0 151650.0 ; - RECT 44100.0 149700.0 45300.0 148500.0 ; - RECT 47100.0 147000.0 48300.0 145800.0 ; - RECT 46500.0 143550.0 45300.0 142350.0 ; - RECT 44100.0 152250.0 42900.0 151050.0 ; - RECT 42900.0 147000.0 44100.0 145800.0 ; - RECT 48300.0 147000.0 47100.0 145800.0 ; - RECT 45300.0 149700.0 44100.0 148500.0 ; - RECT 44100.0 147000.0 42900.0 145800.0 ; - RECT 50700.0 140850.0 36300.0 139950.0 ; - RECT 50700.0 154650.0 36300.0 153750.0 ; - RECT 61650.0 109500.0 62850.0 110700.0 ; - RECT 80250.0 105000.0 81450.0 106200.0 ; - RECT 58650.0 123300.0 59850.0 124500.0 ; - RECT 77250.0 119400.0 78450.0 120600.0 ; - RECT 80250.0 128100.0 81450.0 129300.0 ; - RECT 55650.0 128100.0 56850.0 129300.0 ; - RECT 77250.0 141900.0 78450.0 143100.0 ; - RECT 52650.0 141900.0 53850.0 143100.0 ; - RECT 61650.0 106200.0 62850.0 107400.0 ; - RECT 58650.0 103500.0 59850.0 104700.0 ; - RECT 55650.0 118200.0 56850.0 119400.0 ; - RECT 58650.0 120900.0 59850.0 122100.0 ; - RECT 61650.0 133800.0 62850.0 135000.0 ; - RECT 52650.0 131100.0 53850.0 132300.0 ; - RECT 55650.0 145800.0 56850.0 147000.0 ; - RECT 52650.0 148500.0 53850.0 149700.0 ; - RECT 30450.0 105150.0 26700.0 106050.0 ; - RECT 30450.0 119550.0 26700.0 120450.0 ; - RECT 30450.0 132750.0 26700.0 133650.0 ; - RECT 30450.0 147150.0 26700.0 148050.0 ; - RECT 81300.0 112350.0 26700.0 113250.0 ; - RECT 81300.0 139950.0 26700.0 140850.0 ; - RECT 81300.0 98550.0 26700.0 99450.0 ; - RECT 81300.0 126150.0 26700.0 127050.0 ; - RECT 81300.0 153750.0 26700.0 154650.0 ; - RECT 65250.0 160350.0 64350.0 161250.0 ; - RECT 65250.0 164850.0 64350.0 165750.0 ; - RECT 69450.0 160350.0 64800.0 161250.0 ; - RECT 65250.0 160800.0 64350.0 165300.0 ; - RECT 64800.0 164850.0 62250.0 165750.0 ; - RECT 80850.0 160350.0 72900.0 161250.0 ; - RECT 65250.0 174750.0 64350.0 175650.0 ; - RECT 65250.0 178650.0 64350.0 179550.0 ; - RECT 69450.0 174750.0 64800.0 175650.0 ; - RECT 65250.0 175200.0 64350.0 179100.0 ; - RECT 64800.0 178650.0 59250.0 179550.0 ; - RECT 77850.0 174750.0 72900.0 175650.0 ; - RECT 80850.0 183450.0 56250.0 184350.0 ; - RECT 77850.0 197250.0 53250.0 198150.0 ; - RECT 62250.0 161550.0 48300.0 162450.0 ; - RECT 59250.0 158850.0 45300.0 159750.0 ; - RECT 56250.0 173550.0 48300.0 174450.0 ; - RECT 59250.0 176250.0 45300.0 177150.0 ; - RECT 62250.0 189150.0 48300.0 190050.0 ; - RECT 53250.0 186450.0 45300.0 187350.0 ; - RECT 56250.0 201150.0 48300.0 202050.0 ; - RECT 53250.0 203850.0 45300.0 204750.0 ; - RECT 38850.0 161550.0 37950.0 162450.0 ; - RECT 38850.0 160350.0 37950.0 161250.0 ; - RECT 42900.0 161550.0 38400.0 162450.0 ; - RECT 38850.0 160800.0 37950.0 162000.0 ; - RECT 38400.0 160350.0 33900.0 161250.0 ; - RECT 38850.0 173550.0 37950.0 174450.0 ; - RECT 38850.0 174750.0 37950.0 175650.0 ; - RECT 42900.0 173550.0 38400.0 174450.0 ; - RECT 38850.0 174000.0 37950.0 175200.0 ; - RECT 38400.0 174750.0 33900.0 175650.0 ; - RECT 38850.0 189150.0 37950.0 190050.0 ; - RECT 38850.0 187950.0 37950.0 188850.0 ; - RECT 42900.0 189150.0 38400.0 190050.0 ; - RECT 38850.0 188400.0 37950.0 189600.0 ; - RECT 38400.0 187950.0 33900.0 188850.0 ; - RECT 38850.0 201150.0 37950.0 202050.0 ; - RECT 38850.0 202350.0 37950.0 203250.0 ; - RECT 42900.0 201150.0 38400.0 202050.0 ; - RECT 38850.0 201600.0 37950.0 202800.0 ; - RECT 38400.0 202350.0 33900.0 203250.0 ; - RECT 68700.0 166050.0 67500.0 168000.0 ; - RECT 68700.0 154200.0 67500.0 156150.0 ; - RECT 73500.0 155550.0 72300.0 153750.0 ; - RECT 73500.0 164850.0 72300.0 168450.0 ; - RECT 70800.0 155550.0 69900.0 164850.0 ; - RECT 73500.0 164850.0 72300.0 166050.0 ; - RECT 71100.0 164850.0 69900.0 166050.0 ; - RECT 71100.0 164850.0 69900.0 166050.0 ; - RECT 73500.0 164850.0 72300.0 166050.0 ; - RECT 73500.0 155550.0 72300.0 156750.0 ; - RECT 71100.0 155550.0 69900.0 156750.0 ; - RECT 71100.0 155550.0 69900.0 156750.0 ; - RECT 73500.0 155550.0 72300.0 156750.0 ; - RECT 68700.0 165450.0 67500.0 166650.0 ; - RECT 68700.0 155550.0 67500.0 156750.0 ; - RECT 72900.0 160200.0 71700.0 161400.0 ; - RECT 72900.0 160200.0 71700.0 161400.0 ; - RECT 70350.0 160350.0 69450.0 161250.0 ; - RECT 75300.0 167550.0 65700.0 168450.0 ; - RECT 75300.0 153750.0 65700.0 154650.0 ; - RECT 68700.0 169950.0 67500.0 168000.0 ; - RECT 68700.0 181800.0 67500.0 179850.0 ; - RECT 73500.0 180450.0 72300.0 182250.0 ; - RECT 73500.0 171150.0 72300.0 167550.0 ; - RECT 70800.0 180450.0 69900.0 171150.0 ; - RECT 73500.0 171150.0 72300.0 169950.0 ; - RECT 71100.0 171150.0 69900.0 169950.0 ; - RECT 71100.0 171150.0 69900.0 169950.0 ; - RECT 73500.0 171150.0 72300.0 169950.0 ; - RECT 73500.0 180450.0 72300.0 179250.0 ; - RECT 71100.0 180450.0 69900.0 179250.0 ; - RECT 71100.0 180450.0 69900.0 179250.0 ; - RECT 73500.0 180450.0 72300.0 179250.0 ; - RECT 68700.0 170550.0 67500.0 169350.0 ; - RECT 68700.0 180450.0 67500.0 179250.0 ; - RECT 72900.0 175800.0 71700.0 174600.0 ; - RECT 72900.0 175800.0 71700.0 174600.0 ; - RECT 70350.0 175650.0 69450.0 174750.0 ; - RECT 75300.0 168450.0 65700.0 167550.0 ; - RECT 75300.0 182250.0 65700.0 181350.0 ; - RECT 29700.0 166050.0 28500.0 168000.0 ; - RECT 29700.0 154200.0 28500.0 156150.0 ; - RECT 34500.0 155550.0 33300.0 153750.0 ; - RECT 34500.0 164850.0 33300.0 168450.0 ; - RECT 31800.0 155550.0 30900.0 164850.0 ; - RECT 34500.0 164850.0 33300.0 166050.0 ; - RECT 32100.0 164850.0 30900.0 166050.0 ; - RECT 32100.0 164850.0 30900.0 166050.0 ; - RECT 34500.0 164850.0 33300.0 166050.0 ; - RECT 34500.0 155550.0 33300.0 156750.0 ; - RECT 32100.0 155550.0 30900.0 156750.0 ; - RECT 32100.0 155550.0 30900.0 156750.0 ; - RECT 34500.0 155550.0 33300.0 156750.0 ; - RECT 29700.0 165450.0 28500.0 166650.0 ; - RECT 29700.0 155550.0 28500.0 156750.0 ; - RECT 33900.0 160200.0 32700.0 161400.0 ; - RECT 33900.0 160200.0 32700.0 161400.0 ; - RECT 31350.0 160350.0 30450.0 161250.0 ; - RECT 36300.0 167550.0 26700.0 168450.0 ; - RECT 36300.0 153750.0 26700.0 154650.0 ; - RECT 29700.0 169950.0 28500.0 168000.0 ; - RECT 29700.0 181800.0 28500.0 179850.0 ; - RECT 34500.0 180450.0 33300.0 182250.0 ; - RECT 34500.0 171150.0 33300.0 167550.0 ; - RECT 31800.0 180450.0 30900.0 171150.0 ; - RECT 34500.0 171150.0 33300.0 169950.0 ; - RECT 32100.0 171150.0 30900.0 169950.0 ; - RECT 32100.0 171150.0 30900.0 169950.0 ; - RECT 34500.0 171150.0 33300.0 169950.0 ; - RECT 34500.0 180450.0 33300.0 179250.0 ; - RECT 32100.0 180450.0 30900.0 179250.0 ; - RECT 32100.0 180450.0 30900.0 179250.0 ; - RECT 34500.0 180450.0 33300.0 179250.0 ; - RECT 29700.0 170550.0 28500.0 169350.0 ; - RECT 29700.0 180450.0 28500.0 179250.0 ; - RECT 33900.0 175800.0 32700.0 174600.0 ; - RECT 33900.0 175800.0 32700.0 174600.0 ; - RECT 31350.0 175650.0 30450.0 174750.0 ; - RECT 36300.0 168450.0 26700.0 167550.0 ; - RECT 36300.0 182250.0 26700.0 181350.0 ; - RECT 29700.0 193650.0 28500.0 195600.0 ; - RECT 29700.0 181800.0 28500.0 183750.0 ; - RECT 34500.0 183150.0 33300.0 181350.0 ; - RECT 34500.0 192450.0 33300.0 196050.0 ; - RECT 31800.0 183150.0 30900.0 192450.0 ; - RECT 34500.0 192450.0 33300.0 193650.0 ; - RECT 32100.0 192450.0 30900.0 193650.0 ; - RECT 32100.0 192450.0 30900.0 193650.0 ; - RECT 34500.0 192450.0 33300.0 193650.0 ; - RECT 34500.0 183150.0 33300.0 184350.0 ; - RECT 32100.0 183150.0 30900.0 184350.0 ; - RECT 32100.0 183150.0 30900.0 184350.0 ; - RECT 34500.0 183150.0 33300.0 184350.0 ; - RECT 29700.0 193050.0 28500.0 194250.0 ; - RECT 29700.0 183150.0 28500.0 184350.0 ; - RECT 33900.0 187800.0 32700.0 189000.0 ; - RECT 33900.0 187800.0 32700.0 189000.0 ; - RECT 31350.0 187950.0 30450.0 188850.0 ; - RECT 36300.0 195150.0 26700.0 196050.0 ; - RECT 36300.0 181350.0 26700.0 182250.0 ; - RECT 29700.0 197550.0 28500.0 195600.0 ; - RECT 29700.0 209400.0 28500.0 207450.0 ; - RECT 34500.0 208050.0 33300.0 209850.0 ; - RECT 34500.0 198750.0 33300.0 195150.0 ; - RECT 31800.0 208050.0 30900.0 198750.0 ; - RECT 34500.0 198750.0 33300.0 197550.0 ; - RECT 32100.0 198750.0 30900.0 197550.0 ; - RECT 32100.0 198750.0 30900.0 197550.0 ; - RECT 34500.0 198750.0 33300.0 197550.0 ; - RECT 34500.0 208050.0 33300.0 206850.0 ; - RECT 32100.0 208050.0 30900.0 206850.0 ; - RECT 32100.0 208050.0 30900.0 206850.0 ; - RECT 34500.0 208050.0 33300.0 206850.0 ; - RECT 29700.0 198150.0 28500.0 196950.0 ; - RECT 29700.0 208050.0 28500.0 206850.0 ; - RECT 33900.0 203400.0 32700.0 202200.0 ; - RECT 33900.0 203400.0 32700.0 202200.0 ; - RECT 31350.0 203250.0 30450.0 202350.0 ; - RECT 36300.0 196050.0 26700.0 195150.0 ; - RECT 36300.0 209850.0 26700.0 208950.0 ; - RECT 48900.0 156150.0 47700.0 153750.0 ; - RECT 48900.0 164850.0 47700.0 168450.0 ; - RECT 44100.0 164850.0 42900.0 168450.0 ; - RECT 41700.0 166050.0 40500.0 168000.0 ; - RECT 41700.0 154200.0 40500.0 156150.0 ; - RECT 48900.0 164850.0 47700.0 166050.0 ; - RECT 46500.0 164850.0 45300.0 166050.0 ; - RECT 46500.0 164850.0 45300.0 166050.0 ; - RECT 48900.0 164850.0 47700.0 166050.0 ; - RECT 46500.0 164850.0 45300.0 166050.0 ; - RECT 44100.0 164850.0 42900.0 166050.0 ; - RECT 44100.0 164850.0 42900.0 166050.0 ; - RECT 46500.0 164850.0 45300.0 166050.0 ; - RECT 48900.0 156150.0 47700.0 157350.0 ; - RECT 46500.0 156150.0 45300.0 157350.0 ; - RECT 46500.0 156150.0 45300.0 157350.0 ; - RECT 48900.0 156150.0 47700.0 157350.0 ; - RECT 46500.0 156150.0 45300.0 157350.0 ; - RECT 44100.0 156150.0 42900.0 157350.0 ; - RECT 44100.0 156150.0 42900.0 157350.0 ; - RECT 46500.0 156150.0 45300.0 157350.0 ; - RECT 41700.0 165450.0 40500.0 166650.0 ; - RECT 41700.0 155550.0 40500.0 156750.0 ; - RECT 44100.0 158700.0 45300.0 159900.0 ; - RECT 47100.0 161400.0 48300.0 162600.0 ; - RECT 46500.0 164850.0 45300.0 166050.0 ; - RECT 44100.0 156150.0 42900.0 157350.0 ; - RECT 42900.0 161400.0 44100.0 162600.0 ; - RECT 48300.0 161400.0 47100.0 162600.0 ; - RECT 45300.0 158700.0 44100.0 159900.0 ; - RECT 44100.0 161400.0 42900.0 162600.0 ; - RECT 50700.0 167550.0 36300.0 168450.0 ; - RECT 50700.0 153750.0 36300.0 154650.0 ; - RECT 48900.0 179850.0 47700.0 182250.0 ; - RECT 48900.0 171150.0 47700.0 167550.0 ; - RECT 44100.0 171150.0 42900.0 167550.0 ; - RECT 41700.0 169950.0 40500.0 168000.0 ; - RECT 41700.0 181800.0 40500.0 179850.0 ; - RECT 48900.0 171150.0 47700.0 169950.0 ; - RECT 46500.0 171150.0 45300.0 169950.0 ; - RECT 46500.0 171150.0 45300.0 169950.0 ; - RECT 48900.0 171150.0 47700.0 169950.0 ; - RECT 46500.0 171150.0 45300.0 169950.0 ; - RECT 44100.0 171150.0 42900.0 169950.0 ; - RECT 44100.0 171150.0 42900.0 169950.0 ; - RECT 46500.0 171150.0 45300.0 169950.0 ; - RECT 48900.0 179850.0 47700.0 178650.0 ; - RECT 46500.0 179850.0 45300.0 178650.0 ; - RECT 46500.0 179850.0 45300.0 178650.0 ; - RECT 48900.0 179850.0 47700.0 178650.0 ; - RECT 46500.0 179850.0 45300.0 178650.0 ; - RECT 44100.0 179850.0 42900.0 178650.0 ; - RECT 44100.0 179850.0 42900.0 178650.0 ; - RECT 46500.0 179850.0 45300.0 178650.0 ; - RECT 41700.0 170550.0 40500.0 169350.0 ; - RECT 41700.0 180450.0 40500.0 179250.0 ; - RECT 44100.0 177300.0 45300.0 176100.0 ; - RECT 47100.0 174600.0 48300.0 173400.0 ; - RECT 46500.0 171150.0 45300.0 169950.0 ; - RECT 44100.0 179850.0 42900.0 178650.0 ; - RECT 42900.0 174600.0 44100.0 173400.0 ; - RECT 48300.0 174600.0 47100.0 173400.0 ; - RECT 45300.0 177300.0 44100.0 176100.0 ; - RECT 44100.0 174600.0 42900.0 173400.0 ; - RECT 50700.0 168450.0 36300.0 167550.0 ; - RECT 50700.0 182250.0 36300.0 181350.0 ; - RECT 48900.0 183750.0 47700.0 181350.0 ; - RECT 48900.0 192450.0 47700.0 196050.0 ; - RECT 44100.0 192450.0 42900.0 196050.0 ; - RECT 41700.0 193650.0 40500.0 195600.0 ; - RECT 41700.0 181800.0 40500.0 183750.0 ; - RECT 48900.0 192450.0 47700.0 193650.0 ; - RECT 46500.0 192450.0 45300.0 193650.0 ; - RECT 46500.0 192450.0 45300.0 193650.0 ; - RECT 48900.0 192450.0 47700.0 193650.0 ; - RECT 46500.0 192450.0 45300.0 193650.0 ; - RECT 44100.0 192450.0 42900.0 193650.0 ; - RECT 44100.0 192450.0 42900.0 193650.0 ; - RECT 46500.0 192450.0 45300.0 193650.0 ; - RECT 48900.0 183750.0 47700.0 184950.0 ; - RECT 46500.0 183750.0 45300.0 184950.0 ; - RECT 46500.0 183750.0 45300.0 184950.0 ; - RECT 48900.0 183750.0 47700.0 184950.0 ; - RECT 46500.0 183750.0 45300.0 184950.0 ; - RECT 44100.0 183750.0 42900.0 184950.0 ; - RECT 44100.0 183750.0 42900.0 184950.0 ; - RECT 46500.0 183750.0 45300.0 184950.0 ; - RECT 41700.0 193050.0 40500.0 194250.0 ; - RECT 41700.0 183150.0 40500.0 184350.0 ; - RECT 44100.0 186300.0 45300.0 187500.0 ; - RECT 47100.0 189000.0 48300.0 190200.0 ; - RECT 46500.0 192450.0 45300.0 193650.0 ; - RECT 44100.0 183750.0 42900.0 184950.0 ; - RECT 42900.0 189000.0 44100.0 190200.0 ; - RECT 48300.0 189000.0 47100.0 190200.0 ; - RECT 45300.0 186300.0 44100.0 187500.0 ; - RECT 44100.0 189000.0 42900.0 190200.0 ; - RECT 50700.0 195150.0 36300.0 196050.0 ; - RECT 50700.0 181350.0 36300.0 182250.0 ; - RECT 48900.0 207450.0 47700.0 209850.0 ; - RECT 48900.0 198750.0 47700.0 195150.0 ; - RECT 44100.0 198750.0 42900.0 195150.0 ; - RECT 41700.0 197550.0 40500.0 195600.0 ; - RECT 41700.0 209400.0 40500.0 207450.0 ; - RECT 48900.0 198750.0 47700.0 197550.0 ; - RECT 46500.0 198750.0 45300.0 197550.0 ; - RECT 46500.0 198750.0 45300.0 197550.0 ; - RECT 48900.0 198750.0 47700.0 197550.0 ; - RECT 46500.0 198750.0 45300.0 197550.0 ; - RECT 44100.0 198750.0 42900.0 197550.0 ; - RECT 44100.0 198750.0 42900.0 197550.0 ; - RECT 46500.0 198750.0 45300.0 197550.0 ; - RECT 48900.0 207450.0 47700.0 206250.0 ; - RECT 46500.0 207450.0 45300.0 206250.0 ; - RECT 46500.0 207450.0 45300.0 206250.0 ; - RECT 48900.0 207450.0 47700.0 206250.0 ; - RECT 46500.0 207450.0 45300.0 206250.0 ; - RECT 44100.0 207450.0 42900.0 206250.0 ; - RECT 44100.0 207450.0 42900.0 206250.0 ; - RECT 46500.0 207450.0 45300.0 206250.0 ; - RECT 41700.0 198150.0 40500.0 196950.0 ; - RECT 41700.0 208050.0 40500.0 206850.0 ; - RECT 44100.0 204900.0 45300.0 203700.0 ; - RECT 47100.0 202200.0 48300.0 201000.0 ; - RECT 46500.0 198750.0 45300.0 197550.0 ; - RECT 44100.0 207450.0 42900.0 206250.0 ; - RECT 42900.0 202200.0 44100.0 201000.0 ; - RECT 48300.0 202200.0 47100.0 201000.0 ; - RECT 45300.0 204900.0 44100.0 203700.0 ; - RECT 44100.0 202200.0 42900.0 201000.0 ; - RECT 50700.0 196050.0 36300.0 195150.0 ; - RECT 50700.0 209850.0 36300.0 208950.0 ; - RECT 61650.0 164700.0 62850.0 165900.0 ; - RECT 80250.0 160200.0 81450.0 161400.0 ; - RECT 58650.0 178500.0 59850.0 179700.0 ; - RECT 77250.0 174600.0 78450.0 175800.0 ; - RECT 80250.0 183300.0 81450.0 184500.0 ; - RECT 55650.0 183300.0 56850.0 184500.0 ; - RECT 77250.0 197100.0 78450.0 198300.0 ; - RECT 52650.0 197100.0 53850.0 198300.0 ; - RECT 61650.0 161400.0 62850.0 162600.0 ; - RECT 58650.0 158700.0 59850.0 159900.0 ; - RECT 55650.0 173400.0 56850.0 174600.0 ; - RECT 58650.0 176100.0 59850.0 177300.0 ; - RECT 61650.0 189000.0 62850.0 190200.0 ; - RECT 52650.0 186300.0 53850.0 187500.0 ; - RECT 55650.0 201000.0 56850.0 202200.0 ; - RECT 52650.0 203700.0 53850.0 204900.0 ; - RECT 30450.0 160350.0 26700.0 161250.0 ; - RECT 30450.0 174750.0 26700.0 175650.0 ; - RECT 30450.0 187950.0 26700.0 188850.0 ; - RECT 30450.0 202350.0 26700.0 203250.0 ; - RECT 81300.0 167550.0 26700.0 168450.0 ; - RECT 81300.0 195150.0 26700.0 196050.0 ; - RECT 81300.0 153750.0 26700.0 154650.0 ; - RECT 81300.0 181350.0 26700.0 182250.0 ; - RECT 81300.0 208950.0 26700.0 209850.0 ; - RECT 28500.0 211350.0 29700.0 208950.0 ; - RECT 28500.0 220050.0 29700.0 223650.0 ; - RECT 33300.0 220050.0 34500.0 223650.0 ; - RECT 35700.0 221250.0 36900.0 223200.0 ; - RECT 35700.0 209400.0 36900.0 211350.0 ; - RECT 28500.0 220050.0 29700.0 221250.0 ; - RECT 30900.0 220050.0 32100.0 221250.0 ; - RECT 30900.0 220050.0 32100.0 221250.0 ; - RECT 28500.0 220050.0 29700.0 221250.0 ; - RECT 30900.0 220050.0 32100.0 221250.0 ; - RECT 33300.0 220050.0 34500.0 221250.0 ; - RECT 33300.0 220050.0 34500.0 221250.0 ; - RECT 30900.0 220050.0 32100.0 221250.0 ; - RECT 28500.0 211350.0 29700.0 212550.0 ; - RECT 30900.0 211350.0 32100.0 212550.0 ; - RECT 30900.0 211350.0 32100.0 212550.0 ; - RECT 28500.0 211350.0 29700.0 212550.0 ; - RECT 30900.0 211350.0 32100.0 212550.0 ; - RECT 33300.0 211350.0 34500.0 212550.0 ; - RECT 33300.0 211350.0 34500.0 212550.0 ; - RECT 30900.0 211350.0 32100.0 212550.0 ; - RECT 35700.0 220650.0 36900.0 221850.0 ; - RECT 35700.0 210750.0 36900.0 211950.0 ; - RECT 33300.0 213900.0 32100.0 215100.0 ; - RECT 30300.0 216600.0 29100.0 217800.0 ; - RECT 30900.0 220050.0 32100.0 221250.0 ; - RECT 33300.0 211350.0 34500.0 212550.0 ; - RECT 34500.0 216600.0 33300.0 217800.0 ; - RECT 29100.0 216600.0 30300.0 217800.0 ; - RECT 32100.0 213900.0 33300.0 215100.0 ; - RECT 33300.0 216600.0 34500.0 217800.0 ; - RECT 26700.0 222750.0 41100.0 223650.0 ; - RECT 26700.0 208950.0 41100.0 209850.0 ; - RECT 28500.0 235050.0 29700.0 237450.0 ; - RECT 28500.0 226350.0 29700.0 222750.0 ; - RECT 33300.0 226350.0 34500.0 222750.0 ; - RECT 35700.0 225150.0 36900.0 223200.0 ; - RECT 35700.0 237000.0 36900.0 235050.0 ; - RECT 28500.0 226350.0 29700.0 225150.0 ; - RECT 30900.0 226350.0 32100.0 225150.0 ; - RECT 30900.0 226350.0 32100.0 225150.0 ; - RECT 28500.0 226350.0 29700.0 225150.0 ; - RECT 30900.0 226350.0 32100.0 225150.0 ; - RECT 33300.0 226350.0 34500.0 225150.0 ; - RECT 33300.0 226350.0 34500.0 225150.0 ; - RECT 30900.0 226350.0 32100.0 225150.0 ; - RECT 28500.0 235050.0 29700.0 233850.0 ; - RECT 30900.0 235050.0 32100.0 233850.0 ; - RECT 30900.0 235050.0 32100.0 233850.0 ; - RECT 28500.0 235050.0 29700.0 233850.0 ; - RECT 30900.0 235050.0 32100.0 233850.0 ; - RECT 33300.0 235050.0 34500.0 233850.0 ; - RECT 33300.0 235050.0 34500.0 233850.0 ; - RECT 30900.0 235050.0 32100.0 233850.0 ; - RECT 35700.0 225750.0 36900.0 224550.0 ; - RECT 35700.0 235650.0 36900.0 234450.0 ; - RECT 33300.0 232500.0 32100.0 231300.0 ; - RECT 30300.0 229800.0 29100.0 228600.0 ; - RECT 30900.0 226350.0 32100.0 225150.0 ; - RECT 33300.0 235050.0 34500.0 233850.0 ; - RECT 34500.0 229800.0 33300.0 228600.0 ; - RECT 29100.0 229800.0 30300.0 228600.0 ; - RECT 32100.0 232500.0 33300.0 231300.0 ; - RECT 33300.0 229800.0 34500.0 228600.0 ; - RECT 26700.0 223650.0 41100.0 222750.0 ; - RECT 26700.0 237450.0 41100.0 236550.0 ; - RECT 28500.0 238950.0 29700.0 236550.0 ; - RECT 28500.0 247650.0 29700.0 251250.0 ; - RECT 33300.0 247650.0 34500.0 251250.0 ; - RECT 35700.0 248850.0 36900.0 250800.0 ; - RECT 35700.0 237000.0 36900.0 238950.0 ; - RECT 28500.0 247650.0 29700.0 248850.0 ; - RECT 30900.0 247650.0 32100.0 248850.0 ; - RECT 30900.0 247650.0 32100.0 248850.0 ; - RECT 28500.0 247650.0 29700.0 248850.0 ; - RECT 30900.0 247650.0 32100.0 248850.0 ; - RECT 33300.0 247650.0 34500.0 248850.0 ; - RECT 33300.0 247650.0 34500.0 248850.0 ; - RECT 30900.0 247650.0 32100.0 248850.0 ; - RECT 28500.0 238950.0 29700.0 240150.0 ; - RECT 30900.0 238950.0 32100.0 240150.0 ; - RECT 30900.0 238950.0 32100.0 240150.0 ; - RECT 28500.0 238950.0 29700.0 240150.0 ; - RECT 30900.0 238950.0 32100.0 240150.0 ; - RECT 33300.0 238950.0 34500.0 240150.0 ; - RECT 33300.0 238950.0 34500.0 240150.0 ; - RECT 30900.0 238950.0 32100.0 240150.0 ; - RECT 35700.0 248250.0 36900.0 249450.0 ; - RECT 35700.0 238350.0 36900.0 239550.0 ; - RECT 33300.0 241500.0 32100.0 242700.0 ; - RECT 30300.0 244200.0 29100.0 245400.0 ; - RECT 30900.0 247650.0 32100.0 248850.0 ; - RECT 33300.0 238950.0 34500.0 240150.0 ; - RECT 34500.0 244200.0 33300.0 245400.0 ; - RECT 29100.0 244200.0 30300.0 245400.0 ; - RECT 32100.0 241500.0 33300.0 242700.0 ; - RECT 33300.0 244200.0 34500.0 245400.0 ; - RECT 26700.0 250350.0 41100.0 251250.0 ; - RECT 26700.0 236550.0 41100.0 237450.0 ; - RECT 28500.0 262650.0 29700.0 265050.0 ; - RECT 28500.0 253950.0 29700.0 250350.0 ; - RECT 33300.0 253950.0 34500.0 250350.0 ; - RECT 35700.0 252750.0 36900.0 250800.0 ; - RECT 35700.0 264600.0 36900.0 262650.0 ; - RECT 28500.0 253950.0 29700.0 252750.0 ; - RECT 30900.0 253950.0 32100.0 252750.0 ; - RECT 30900.0 253950.0 32100.0 252750.0 ; - RECT 28500.0 253950.0 29700.0 252750.0 ; - RECT 30900.0 253950.0 32100.0 252750.0 ; - RECT 33300.0 253950.0 34500.0 252750.0 ; - RECT 33300.0 253950.0 34500.0 252750.0 ; - RECT 30900.0 253950.0 32100.0 252750.0 ; - RECT 28500.0 262650.0 29700.0 261450.0 ; - RECT 30900.0 262650.0 32100.0 261450.0 ; - RECT 30900.0 262650.0 32100.0 261450.0 ; - RECT 28500.0 262650.0 29700.0 261450.0 ; - RECT 30900.0 262650.0 32100.0 261450.0 ; - RECT 33300.0 262650.0 34500.0 261450.0 ; - RECT 33300.0 262650.0 34500.0 261450.0 ; - RECT 30900.0 262650.0 32100.0 261450.0 ; - RECT 35700.0 253350.0 36900.0 252150.0 ; - RECT 35700.0 263250.0 36900.0 262050.0 ; - RECT 33300.0 260100.0 32100.0 258900.0 ; - RECT 30300.0 257400.0 29100.0 256200.0 ; - RECT 30900.0 253950.0 32100.0 252750.0 ; - RECT 33300.0 262650.0 34500.0 261450.0 ; - RECT 34500.0 257400.0 33300.0 256200.0 ; - RECT 29100.0 257400.0 30300.0 256200.0 ; - RECT 32100.0 260100.0 33300.0 258900.0 ; - RECT 33300.0 257400.0 34500.0 256200.0 ; - RECT 26700.0 251250.0 41100.0 250350.0 ; - RECT 26700.0 265050.0 41100.0 264150.0 ; - RECT 28500.0 266550.0 29700.0 264150.0 ; - RECT 28500.0 275250.0 29700.0 278850.0 ; - RECT 33300.0 275250.0 34500.0 278850.0 ; - RECT 35700.0 276450.0 36900.0 278400.0 ; - RECT 35700.0 264600.0 36900.0 266550.0 ; - RECT 28500.0 275250.0 29700.0 276450.0 ; - RECT 30900.0 275250.0 32100.0 276450.0 ; - RECT 30900.0 275250.0 32100.0 276450.0 ; - RECT 28500.0 275250.0 29700.0 276450.0 ; - RECT 30900.0 275250.0 32100.0 276450.0 ; - RECT 33300.0 275250.0 34500.0 276450.0 ; - RECT 33300.0 275250.0 34500.0 276450.0 ; - RECT 30900.0 275250.0 32100.0 276450.0 ; - RECT 28500.0 266550.0 29700.0 267750.0 ; - RECT 30900.0 266550.0 32100.0 267750.0 ; - RECT 30900.0 266550.0 32100.0 267750.0 ; - RECT 28500.0 266550.0 29700.0 267750.0 ; - RECT 30900.0 266550.0 32100.0 267750.0 ; - RECT 33300.0 266550.0 34500.0 267750.0 ; - RECT 33300.0 266550.0 34500.0 267750.0 ; - RECT 30900.0 266550.0 32100.0 267750.0 ; - RECT 35700.0 275850.0 36900.0 277050.0 ; - RECT 35700.0 265950.0 36900.0 267150.0 ; - RECT 33300.0 269100.0 32100.0 270300.0 ; - RECT 30300.0 271800.0 29100.0 273000.0 ; - RECT 30900.0 275250.0 32100.0 276450.0 ; - RECT 33300.0 266550.0 34500.0 267750.0 ; - RECT 34500.0 271800.0 33300.0 273000.0 ; - RECT 29100.0 271800.0 30300.0 273000.0 ; - RECT 32100.0 269100.0 33300.0 270300.0 ; - RECT 33300.0 271800.0 34500.0 273000.0 ; - RECT 26700.0 277950.0 41100.0 278850.0 ; - RECT 26700.0 264150.0 41100.0 265050.0 ; - RECT 28500.0 290250.0 29700.0 292650.0 ; - RECT 28500.0 281550.0 29700.0 277950.0 ; - RECT 33300.0 281550.0 34500.0 277950.0 ; - RECT 35700.0 280350.0 36900.0 278400.0 ; - RECT 35700.0 292200.0 36900.0 290250.0 ; - RECT 28500.0 281550.0 29700.0 280350.0 ; - RECT 30900.0 281550.0 32100.0 280350.0 ; - RECT 30900.0 281550.0 32100.0 280350.0 ; - RECT 28500.0 281550.0 29700.0 280350.0 ; - RECT 30900.0 281550.0 32100.0 280350.0 ; - RECT 33300.0 281550.0 34500.0 280350.0 ; - RECT 33300.0 281550.0 34500.0 280350.0 ; - RECT 30900.0 281550.0 32100.0 280350.0 ; - RECT 28500.0 290250.0 29700.0 289050.0 ; - RECT 30900.0 290250.0 32100.0 289050.0 ; - RECT 30900.0 290250.0 32100.0 289050.0 ; - RECT 28500.0 290250.0 29700.0 289050.0 ; - RECT 30900.0 290250.0 32100.0 289050.0 ; - RECT 33300.0 290250.0 34500.0 289050.0 ; - RECT 33300.0 290250.0 34500.0 289050.0 ; - RECT 30900.0 290250.0 32100.0 289050.0 ; - RECT 35700.0 280950.0 36900.0 279750.0 ; - RECT 35700.0 290850.0 36900.0 289650.0 ; - RECT 33300.0 287700.0 32100.0 286500.0 ; - RECT 30300.0 285000.0 29100.0 283800.0 ; - RECT 30900.0 281550.0 32100.0 280350.0 ; - RECT 33300.0 290250.0 34500.0 289050.0 ; - RECT 34500.0 285000.0 33300.0 283800.0 ; - RECT 29100.0 285000.0 30300.0 283800.0 ; - RECT 32100.0 287700.0 33300.0 286500.0 ; - RECT 33300.0 285000.0 34500.0 283800.0 ; - RECT 26700.0 278850.0 41100.0 277950.0 ; - RECT 26700.0 292650.0 41100.0 291750.0 ; - RECT 28500.0 294150.0 29700.0 291750.0 ; - RECT 28500.0 302850.0 29700.0 306450.0 ; - RECT 33300.0 302850.0 34500.0 306450.0 ; - RECT 35700.0 304050.0 36900.0 306000.0 ; - RECT 35700.0 292200.0 36900.0 294150.0 ; - RECT 28500.0 302850.0 29700.0 304050.0 ; - RECT 30900.0 302850.0 32100.0 304050.0 ; - RECT 30900.0 302850.0 32100.0 304050.0 ; - RECT 28500.0 302850.0 29700.0 304050.0 ; - RECT 30900.0 302850.0 32100.0 304050.0 ; - RECT 33300.0 302850.0 34500.0 304050.0 ; - RECT 33300.0 302850.0 34500.0 304050.0 ; - RECT 30900.0 302850.0 32100.0 304050.0 ; - RECT 28500.0 294150.0 29700.0 295350.0 ; - RECT 30900.0 294150.0 32100.0 295350.0 ; - RECT 30900.0 294150.0 32100.0 295350.0 ; - RECT 28500.0 294150.0 29700.0 295350.0 ; - RECT 30900.0 294150.0 32100.0 295350.0 ; - RECT 33300.0 294150.0 34500.0 295350.0 ; - RECT 33300.0 294150.0 34500.0 295350.0 ; - RECT 30900.0 294150.0 32100.0 295350.0 ; - RECT 35700.0 303450.0 36900.0 304650.0 ; - RECT 35700.0 293550.0 36900.0 294750.0 ; - RECT 33300.0 296700.0 32100.0 297900.0 ; - RECT 30300.0 299400.0 29100.0 300600.0 ; - RECT 30900.0 302850.0 32100.0 304050.0 ; - RECT 33300.0 294150.0 34500.0 295350.0 ; - RECT 34500.0 299400.0 33300.0 300600.0 ; - RECT 29100.0 299400.0 30300.0 300600.0 ; - RECT 32100.0 296700.0 33300.0 297900.0 ; - RECT 33300.0 299400.0 34500.0 300600.0 ; - RECT 26700.0 305550.0 41100.0 306450.0 ; - RECT 26700.0 291750.0 41100.0 292650.0 ; - RECT 28500.0 317850.0 29700.0 320250.0 ; - RECT 28500.0 309150.0 29700.0 305550.0 ; - RECT 33300.0 309150.0 34500.0 305550.0 ; - RECT 35700.0 307950.0 36900.0 306000.0 ; - RECT 35700.0 319800.0 36900.0 317850.0 ; - RECT 28500.0 309150.0 29700.0 307950.0 ; - RECT 30900.0 309150.0 32100.0 307950.0 ; - RECT 30900.0 309150.0 32100.0 307950.0 ; - RECT 28500.0 309150.0 29700.0 307950.0 ; - RECT 30900.0 309150.0 32100.0 307950.0 ; - RECT 33300.0 309150.0 34500.0 307950.0 ; - RECT 33300.0 309150.0 34500.0 307950.0 ; - RECT 30900.0 309150.0 32100.0 307950.0 ; - RECT 28500.0 317850.0 29700.0 316650.0 ; - RECT 30900.0 317850.0 32100.0 316650.0 ; - RECT 30900.0 317850.0 32100.0 316650.0 ; - RECT 28500.0 317850.0 29700.0 316650.0 ; - RECT 30900.0 317850.0 32100.0 316650.0 ; - RECT 33300.0 317850.0 34500.0 316650.0 ; - RECT 33300.0 317850.0 34500.0 316650.0 ; - RECT 30900.0 317850.0 32100.0 316650.0 ; - RECT 35700.0 308550.0 36900.0 307350.0 ; - RECT 35700.0 318450.0 36900.0 317250.0 ; - RECT 33300.0 315300.0 32100.0 314100.0 ; - RECT 30300.0 312600.0 29100.0 311400.0 ; - RECT 30900.0 309150.0 32100.0 307950.0 ; - RECT 33300.0 317850.0 34500.0 316650.0 ; - RECT 34500.0 312600.0 33300.0 311400.0 ; - RECT 29100.0 312600.0 30300.0 311400.0 ; - RECT 32100.0 315300.0 33300.0 314100.0 ; - RECT 33300.0 312600.0 34500.0 311400.0 ; - RECT 26700.0 306450.0 41100.0 305550.0 ; - RECT 26700.0 320250.0 41100.0 319350.0 ; - RECT 28500.0 321750.0 29700.0 319350.0 ; - RECT 28500.0 330450.0 29700.0 334050.0 ; - RECT 33300.0 330450.0 34500.0 334050.0 ; - RECT 35700.0 331650.0 36900.0 333600.0 ; - RECT 35700.0 319800.0 36900.0 321750.0 ; - RECT 28500.0 330450.0 29700.0 331650.0 ; - RECT 30900.0 330450.0 32100.0 331650.0 ; - RECT 30900.0 330450.0 32100.0 331650.0 ; - RECT 28500.0 330450.0 29700.0 331650.0 ; - RECT 30900.0 330450.0 32100.0 331650.0 ; - RECT 33300.0 330450.0 34500.0 331650.0 ; - RECT 33300.0 330450.0 34500.0 331650.0 ; - RECT 30900.0 330450.0 32100.0 331650.0 ; - RECT 28500.0 321750.0 29700.0 322950.0 ; - RECT 30900.0 321750.0 32100.0 322950.0 ; - RECT 30900.0 321750.0 32100.0 322950.0 ; - RECT 28500.0 321750.0 29700.0 322950.0 ; - RECT 30900.0 321750.0 32100.0 322950.0 ; - RECT 33300.0 321750.0 34500.0 322950.0 ; - RECT 33300.0 321750.0 34500.0 322950.0 ; - RECT 30900.0 321750.0 32100.0 322950.0 ; - RECT 35700.0 331050.0 36900.0 332250.0 ; - RECT 35700.0 321150.0 36900.0 322350.0 ; - RECT 33300.0 324300.0 32100.0 325500.0 ; - RECT 30300.0 327000.0 29100.0 328200.0 ; - RECT 30900.0 330450.0 32100.0 331650.0 ; - RECT 33300.0 321750.0 34500.0 322950.0 ; - RECT 34500.0 327000.0 33300.0 328200.0 ; - RECT 29100.0 327000.0 30300.0 328200.0 ; - RECT 32100.0 324300.0 33300.0 325500.0 ; - RECT 33300.0 327000.0 34500.0 328200.0 ; - RECT 26700.0 333150.0 41100.0 334050.0 ; - RECT 26700.0 319350.0 41100.0 320250.0 ; - RECT 28500.0 345450.0 29700.0 347850.0 ; - RECT 28500.0 336750.0 29700.0 333150.0 ; - RECT 33300.0 336750.0 34500.0 333150.0 ; - RECT 35700.0 335550.0 36900.0 333600.0 ; - RECT 35700.0 347400.0 36900.0 345450.0 ; - RECT 28500.0 336750.0 29700.0 335550.0 ; - RECT 30900.0 336750.0 32100.0 335550.0 ; - RECT 30900.0 336750.0 32100.0 335550.0 ; - RECT 28500.0 336750.0 29700.0 335550.0 ; - RECT 30900.0 336750.0 32100.0 335550.0 ; - RECT 33300.0 336750.0 34500.0 335550.0 ; - RECT 33300.0 336750.0 34500.0 335550.0 ; - RECT 30900.0 336750.0 32100.0 335550.0 ; - RECT 28500.0 345450.0 29700.0 344250.0 ; - RECT 30900.0 345450.0 32100.0 344250.0 ; - RECT 30900.0 345450.0 32100.0 344250.0 ; - RECT 28500.0 345450.0 29700.0 344250.0 ; - RECT 30900.0 345450.0 32100.0 344250.0 ; - RECT 33300.0 345450.0 34500.0 344250.0 ; - RECT 33300.0 345450.0 34500.0 344250.0 ; - RECT 30900.0 345450.0 32100.0 344250.0 ; - RECT 35700.0 336150.0 36900.0 334950.0 ; - RECT 35700.0 346050.0 36900.0 344850.0 ; - RECT 33300.0 342900.0 32100.0 341700.0 ; - RECT 30300.0 340200.0 29100.0 339000.0 ; - RECT 30900.0 336750.0 32100.0 335550.0 ; - RECT 33300.0 345450.0 34500.0 344250.0 ; - RECT 34500.0 340200.0 33300.0 339000.0 ; - RECT 29100.0 340200.0 30300.0 339000.0 ; - RECT 32100.0 342900.0 33300.0 341700.0 ; - RECT 33300.0 340200.0 34500.0 339000.0 ; - RECT 26700.0 334050.0 41100.0 333150.0 ; - RECT 26700.0 347850.0 41100.0 346950.0 ; - RECT 28500.0 349350.0 29700.0 346950.0 ; - RECT 28500.0 358050.0 29700.0 361650.0 ; - RECT 33300.0 358050.0 34500.0 361650.0 ; - RECT 35700.0 359250.0 36900.0 361200.0 ; - RECT 35700.0 347400.0 36900.0 349350.0 ; - RECT 28500.0 358050.0 29700.0 359250.0 ; - RECT 30900.0 358050.0 32100.0 359250.0 ; - RECT 30900.0 358050.0 32100.0 359250.0 ; - RECT 28500.0 358050.0 29700.0 359250.0 ; - RECT 30900.0 358050.0 32100.0 359250.0 ; - RECT 33300.0 358050.0 34500.0 359250.0 ; - RECT 33300.0 358050.0 34500.0 359250.0 ; - RECT 30900.0 358050.0 32100.0 359250.0 ; - RECT 28500.0 349350.0 29700.0 350550.0 ; - RECT 30900.0 349350.0 32100.0 350550.0 ; - RECT 30900.0 349350.0 32100.0 350550.0 ; - RECT 28500.0 349350.0 29700.0 350550.0 ; - RECT 30900.0 349350.0 32100.0 350550.0 ; - RECT 33300.0 349350.0 34500.0 350550.0 ; - RECT 33300.0 349350.0 34500.0 350550.0 ; - RECT 30900.0 349350.0 32100.0 350550.0 ; - RECT 35700.0 358650.0 36900.0 359850.0 ; - RECT 35700.0 348750.0 36900.0 349950.0 ; - RECT 33300.0 351900.0 32100.0 353100.0 ; - RECT 30300.0 354600.0 29100.0 355800.0 ; - RECT 30900.0 358050.0 32100.0 359250.0 ; - RECT 33300.0 349350.0 34500.0 350550.0 ; - RECT 34500.0 354600.0 33300.0 355800.0 ; - RECT 29100.0 354600.0 30300.0 355800.0 ; - RECT 32100.0 351900.0 33300.0 353100.0 ; - RECT 33300.0 354600.0 34500.0 355800.0 ; - RECT 26700.0 360750.0 41100.0 361650.0 ; - RECT 26700.0 346950.0 41100.0 347850.0 ; - RECT 28500.0 373050.0 29700.0 375450.0 ; - RECT 28500.0 364350.0 29700.0 360750.0 ; - RECT 33300.0 364350.0 34500.0 360750.0 ; - RECT 35700.0 363150.0 36900.0 361200.0 ; - RECT 35700.0 375000.0 36900.0 373050.0 ; - RECT 28500.0 364350.0 29700.0 363150.0 ; - RECT 30900.0 364350.0 32100.0 363150.0 ; - RECT 30900.0 364350.0 32100.0 363150.0 ; - RECT 28500.0 364350.0 29700.0 363150.0 ; - RECT 30900.0 364350.0 32100.0 363150.0 ; - RECT 33300.0 364350.0 34500.0 363150.0 ; - RECT 33300.0 364350.0 34500.0 363150.0 ; - RECT 30900.0 364350.0 32100.0 363150.0 ; - RECT 28500.0 373050.0 29700.0 371850.0 ; - RECT 30900.0 373050.0 32100.0 371850.0 ; - RECT 30900.0 373050.0 32100.0 371850.0 ; - RECT 28500.0 373050.0 29700.0 371850.0 ; - RECT 30900.0 373050.0 32100.0 371850.0 ; - RECT 33300.0 373050.0 34500.0 371850.0 ; - RECT 33300.0 373050.0 34500.0 371850.0 ; - RECT 30900.0 373050.0 32100.0 371850.0 ; - RECT 35700.0 363750.0 36900.0 362550.0 ; - RECT 35700.0 373650.0 36900.0 372450.0 ; - RECT 33300.0 370500.0 32100.0 369300.0 ; - RECT 30300.0 367800.0 29100.0 366600.0 ; - RECT 30900.0 364350.0 32100.0 363150.0 ; - RECT 33300.0 373050.0 34500.0 371850.0 ; - RECT 34500.0 367800.0 33300.0 366600.0 ; - RECT 29100.0 367800.0 30300.0 366600.0 ; - RECT 32100.0 370500.0 33300.0 369300.0 ; - RECT 33300.0 367800.0 34500.0 366600.0 ; - RECT 26700.0 361650.0 41100.0 360750.0 ; - RECT 26700.0 375450.0 41100.0 374550.0 ; - RECT 28500.0 376950.0 29700.0 374550.0 ; - RECT 28500.0 385650.0 29700.0 389250.0 ; - RECT 33300.0 385650.0 34500.0 389250.0 ; - RECT 35700.0 386850.0 36900.0 388800.0 ; - RECT 35700.0 375000.0 36900.0 376950.0 ; - RECT 28500.0 385650.0 29700.0 386850.0 ; - RECT 30900.0 385650.0 32100.0 386850.0 ; - RECT 30900.0 385650.0 32100.0 386850.0 ; - RECT 28500.0 385650.0 29700.0 386850.0 ; - RECT 30900.0 385650.0 32100.0 386850.0 ; - RECT 33300.0 385650.0 34500.0 386850.0 ; - RECT 33300.0 385650.0 34500.0 386850.0 ; - RECT 30900.0 385650.0 32100.0 386850.0 ; - RECT 28500.0 376950.0 29700.0 378150.0 ; - RECT 30900.0 376950.0 32100.0 378150.0 ; - RECT 30900.0 376950.0 32100.0 378150.0 ; - RECT 28500.0 376950.0 29700.0 378150.0 ; - RECT 30900.0 376950.0 32100.0 378150.0 ; - RECT 33300.0 376950.0 34500.0 378150.0 ; - RECT 33300.0 376950.0 34500.0 378150.0 ; - RECT 30900.0 376950.0 32100.0 378150.0 ; - RECT 35700.0 386250.0 36900.0 387450.0 ; - RECT 35700.0 376350.0 36900.0 377550.0 ; - RECT 33300.0 379500.0 32100.0 380700.0 ; - RECT 30300.0 382200.0 29100.0 383400.0 ; - RECT 30900.0 385650.0 32100.0 386850.0 ; - RECT 33300.0 376950.0 34500.0 378150.0 ; - RECT 34500.0 382200.0 33300.0 383400.0 ; - RECT 29100.0 382200.0 30300.0 383400.0 ; - RECT 32100.0 379500.0 33300.0 380700.0 ; - RECT 33300.0 382200.0 34500.0 383400.0 ; - RECT 26700.0 388350.0 41100.0 389250.0 ; - RECT 26700.0 374550.0 41100.0 375450.0 ; - RECT 28500.0 400650.0 29700.0 403050.0 ; - RECT 28500.0 391950.0 29700.0 388350.0 ; - RECT 33300.0 391950.0 34500.0 388350.0 ; - RECT 35700.0 390750.0 36900.0 388800.0 ; - RECT 35700.0 402600.0 36900.0 400650.0 ; - RECT 28500.0 391950.0 29700.0 390750.0 ; - RECT 30900.0 391950.0 32100.0 390750.0 ; - RECT 30900.0 391950.0 32100.0 390750.0 ; - RECT 28500.0 391950.0 29700.0 390750.0 ; - RECT 30900.0 391950.0 32100.0 390750.0 ; - RECT 33300.0 391950.0 34500.0 390750.0 ; - RECT 33300.0 391950.0 34500.0 390750.0 ; - RECT 30900.0 391950.0 32100.0 390750.0 ; - RECT 28500.0 400650.0 29700.0 399450.0 ; - RECT 30900.0 400650.0 32100.0 399450.0 ; - RECT 30900.0 400650.0 32100.0 399450.0 ; - RECT 28500.0 400650.0 29700.0 399450.0 ; - RECT 30900.0 400650.0 32100.0 399450.0 ; - RECT 33300.0 400650.0 34500.0 399450.0 ; - RECT 33300.0 400650.0 34500.0 399450.0 ; - RECT 30900.0 400650.0 32100.0 399450.0 ; - RECT 35700.0 391350.0 36900.0 390150.0 ; - RECT 35700.0 401250.0 36900.0 400050.0 ; - RECT 33300.0 398100.0 32100.0 396900.0 ; - RECT 30300.0 395400.0 29100.0 394200.0 ; - RECT 30900.0 391950.0 32100.0 390750.0 ; - RECT 33300.0 400650.0 34500.0 399450.0 ; - RECT 34500.0 395400.0 33300.0 394200.0 ; - RECT 29100.0 395400.0 30300.0 394200.0 ; - RECT 32100.0 398100.0 33300.0 396900.0 ; - RECT 33300.0 395400.0 34500.0 394200.0 ; - RECT 26700.0 389250.0 41100.0 388350.0 ; - RECT 26700.0 403050.0 41100.0 402150.0 ; - RECT 28500.0 404550.0 29700.0 402150.0 ; - RECT 28500.0 413250.0 29700.0 416850.0 ; - RECT 33300.0 413250.0 34500.0 416850.0 ; - RECT 35700.0 414450.0 36900.0 416400.0 ; - RECT 35700.0 402600.0 36900.0 404550.0 ; - RECT 28500.0 413250.0 29700.0 414450.0 ; - RECT 30900.0 413250.0 32100.0 414450.0 ; - RECT 30900.0 413250.0 32100.0 414450.0 ; - RECT 28500.0 413250.0 29700.0 414450.0 ; - RECT 30900.0 413250.0 32100.0 414450.0 ; - RECT 33300.0 413250.0 34500.0 414450.0 ; - RECT 33300.0 413250.0 34500.0 414450.0 ; - RECT 30900.0 413250.0 32100.0 414450.0 ; - RECT 28500.0 404550.0 29700.0 405750.0 ; - RECT 30900.0 404550.0 32100.0 405750.0 ; - RECT 30900.0 404550.0 32100.0 405750.0 ; - RECT 28500.0 404550.0 29700.0 405750.0 ; - RECT 30900.0 404550.0 32100.0 405750.0 ; - RECT 33300.0 404550.0 34500.0 405750.0 ; - RECT 33300.0 404550.0 34500.0 405750.0 ; - RECT 30900.0 404550.0 32100.0 405750.0 ; - RECT 35700.0 413850.0 36900.0 415050.0 ; - RECT 35700.0 403950.0 36900.0 405150.0 ; - RECT 33300.0 407100.0 32100.0 408300.0 ; - RECT 30300.0 409800.0 29100.0 411000.0 ; - RECT 30900.0 413250.0 32100.0 414450.0 ; - RECT 33300.0 404550.0 34500.0 405750.0 ; - RECT 34500.0 409800.0 33300.0 411000.0 ; - RECT 29100.0 409800.0 30300.0 411000.0 ; - RECT 32100.0 407100.0 33300.0 408300.0 ; - RECT 33300.0 409800.0 34500.0 411000.0 ; - RECT 26700.0 415950.0 41100.0 416850.0 ; - RECT 26700.0 402150.0 41100.0 403050.0 ; - RECT 28500.0 428250.0 29700.0 430650.0 ; - RECT 28500.0 419550.0 29700.0 415950.0 ; - RECT 33300.0 419550.0 34500.0 415950.0 ; - RECT 35700.0 418350.0 36900.0 416400.0 ; - RECT 35700.0 430200.0 36900.0 428250.0 ; - RECT 28500.0 419550.0 29700.0 418350.0 ; - RECT 30900.0 419550.0 32100.0 418350.0 ; - RECT 30900.0 419550.0 32100.0 418350.0 ; - RECT 28500.0 419550.0 29700.0 418350.0 ; - RECT 30900.0 419550.0 32100.0 418350.0 ; - RECT 33300.0 419550.0 34500.0 418350.0 ; - RECT 33300.0 419550.0 34500.0 418350.0 ; - RECT 30900.0 419550.0 32100.0 418350.0 ; - RECT 28500.0 428250.0 29700.0 427050.0 ; - RECT 30900.0 428250.0 32100.0 427050.0 ; - RECT 30900.0 428250.0 32100.0 427050.0 ; - RECT 28500.0 428250.0 29700.0 427050.0 ; - RECT 30900.0 428250.0 32100.0 427050.0 ; - RECT 33300.0 428250.0 34500.0 427050.0 ; - RECT 33300.0 428250.0 34500.0 427050.0 ; - RECT 30900.0 428250.0 32100.0 427050.0 ; - RECT 35700.0 418950.0 36900.0 417750.0 ; - RECT 35700.0 428850.0 36900.0 427650.0 ; - RECT 33300.0 425700.0 32100.0 424500.0 ; - RECT 30300.0 423000.0 29100.0 421800.0 ; - RECT 30900.0 419550.0 32100.0 418350.0 ; - RECT 33300.0 428250.0 34500.0 427050.0 ; - RECT 34500.0 423000.0 33300.0 421800.0 ; - RECT 29100.0 423000.0 30300.0 421800.0 ; - RECT 32100.0 425700.0 33300.0 424500.0 ; - RECT 33300.0 423000.0 34500.0 421800.0 ; - RECT 26700.0 416850.0 41100.0 415950.0 ; - RECT 26700.0 430650.0 41100.0 429750.0 ; - RECT 47700.0 221250.0 48900.0 223200.0 ; - RECT 47700.0 209400.0 48900.0 211350.0 ; - RECT 42900.0 210750.0 44100.0 208950.0 ; - RECT 42900.0 220050.0 44100.0 223650.0 ; - RECT 45600.0 210750.0 46500.0 220050.0 ; - RECT 42900.0 220050.0 44100.0 221250.0 ; - RECT 45300.0 220050.0 46500.0 221250.0 ; - RECT 45300.0 220050.0 46500.0 221250.0 ; - RECT 42900.0 220050.0 44100.0 221250.0 ; - RECT 42900.0 210750.0 44100.0 211950.0 ; - RECT 45300.0 210750.0 46500.0 211950.0 ; - RECT 45300.0 210750.0 46500.0 211950.0 ; - RECT 42900.0 210750.0 44100.0 211950.0 ; - RECT 47700.0 220650.0 48900.0 221850.0 ; - RECT 47700.0 210750.0 48900.0 211950.0 ; - RECT 43500.0 215400.0 44700.0 216600.0 ; - RECT 43500.0 215400.0 44700.0 216600.0 ; - RECT 46050.0 215550.0 46950.0 216450.0 ; - RECT 41100.0 222750.0 50700.0 223650.0 ; - RECT 41100.0 208950.0 50700.0 209850.0 ; - RECT 47700.0 225150.0 48900.0 223200.0 ; - RECT 47700.0 237000.0 48900.0 235050.0 ; - RECT 42900.0 235650.0 44100.0 237450.0 ; - RECT 42900.0 226350.0 44100.0 222750.0 ; - RECT 45600.0 235650.0 46500.0 226350.0 ; - RECT 42900.0 226350.0 44100.0 225150.0 ; - RECT 45300.0 226350.0 46500.0 225150.0 ; - RECT 45300.0 226350.0 46500.0 225150.0 ; - RECT 42900.0 226350.0 44100.0 225150.0 ; - RECT 42900.0 235650.0 44100.0 234450.0 ; - RECT 45300.0 235650.0 46500.0 234450.0 ; - RECT 45300.0 235650.0 46500.0 234450.0 ; - RECT 42900.0 235650.0 44100.0 234450.0 ; - RECT 47700.0 225750.0 48900.0 224550.0 ; - RECT 47700.0 235650.0 48900.0 234450.0 ; - RECT 43500.0 231000.0 44700.0 229800.0 ; - RECT 43500.0 231000.0 44700.0 229800.0 ; - RECT 46050.0 230850.0 46950.0 229950.0 ; - RECT 41100.0 223650.0 50700.0 222750.0 ; - RECT 41100.0 237450.0 50700.0 236550.0 ; - RECT 47700.0 248850.0 48900.0 250800.0 ; - RECT 47700.0 237000.0 48900.0 238950.0 ; - RECT 42900.0 238350.0 44100.0 236550.0 ; - RECT 42900.0 247650.0 44100.0 251250.0 ; - RECT 45600.0 238350.0 46500.0 247650.0 ; - RECT 42900.0 247650.0 44100.0 248850.0 ; - RECT 45300.0 247650.0 46500.0 248850.0 ; - RECT 45300.0 247650.0 46500.0 248850.0 ; - RECT 42900.0 247650.0 44100.0 248850.0 ; - RECT 42900.0 238350.0 44100.0 239550.0 ; - RECT 45300.0 238350.0 46500.0 239550.0 ; - RECT 45300.0 238350.0 46500.0 239550.0 ; - RECT 42900.0 238350.0 44100.0 239550.0 ; - RECT 47700.0 248250.0 48900.0 249450.0 ; - RECT 47700.0 238350.0 48900.0 239550.0 ; - RECT 43500.0 243000.0 44700.0 244200.0 ; - RECT 43500.0 243000.0 44700.0 244200.0 ; - RECT 46050.0 243150.0 46950.0 244050.0 ; - RECT 41100.0 250350.0 50700.0 251250.0 ; - RECT 41100.0 236550.0 50700.0 237450.0 ; - RECT 47700.0 252750.0 48900.0 250800.0 ; - RECT 47700.0 264600.0 48900.0 262650.0 ; - RECT 42900.0 263250.0 44100.0 265050.0 ; - RECT 42900.0 253950.0 44100.0 250350.0 ; - RECT 45600.0 263250.0 46500.0 253950.0 ; - RECT 42900.0 253950.0 44100.0 252750.0 ; - RECT 45300.0 253950.0 46500.0 252750.0 ; - RECT 45300.0 253950.0 46500.0 252750.0 ; - RECT 42900.0 253950.0 44100.0 252750.0 ; - RECT 42900.0 263250.0 44100.0 262050.0 ; - RECT 45300.0 263250.0 46500.0 262050.0 ; - RECT 45300.0 263250.0 46500.0 262050.0 ; - RECT 42900.0 263250.0 44100.0 262050.0 ; - RECT 47700.0 253350.0 48900.0 252150.0 ; - RECT 47700.0 263250.0 48900.0 262050.0 ; - RECT 43500.0 258600.0 44700.0 257400.0 ; - RECT 43500.0 258600.0 44700.0 257400.0 ; - RECT 46050.0 258450.0 46950.0 257550.0 ; - RECT 41100.0 251250.0 50700.0 250350.0 ; - RECT 41100.0 265050.0 50700.0 264150.0 ; - RECT 47700.0 276450.0 48900.0 278400.0 ; - RECT 47700.0 264600.0 48900.0 266550.0 ; - RECT 42900.0 265950.0 44100.0 264150.0 ; - RECT 42900.0 275250.0 44100.0 278850.0 ; - RECT 45600.0 265950.0 46500.0 275250.0 ; - RECT 42900.0 275250.0 44100.0 276450.0 ; - RECT 45300.0 275250.0 46500.0 276450.0 ; - RECT 45300.0 275250.0 46500.0 276450.0 ; - RECT 42900.0 275250.0 44100.0 276450.0 ; - RECT 42900.0 265950.0 44100.0 267150.0 ; - RECT 45300.0 265950.0 46500.0 267150.0 ; - RECT 45300.0 265950.0 46500.0 267150.0 ; - RECT 42900.0 265950.0 44100.0 267150.0 ; - RECT 47700.0 275850.0 48900.0 277050.0 ; - RECT 47700.0 265950.0 48900.0 267150.0 ; - RECT 43500.0 270600.0 44700.0 271800.0 ; - RECT 43500.0 270600.0 44700.0 271800.0 ; - RECT 46050.0 270750.0 46950.0 271650.0 ; - RECT 41100.0 277950.0 50700.0 278850.0 ; - RECT 41100.0 264150.0 50700.0 265050.0 ; - RECT 47700.0 280350.0 48900.0 278400.0 ; - RECT 47700.0 292200.0 48900.0 290250.0 ; - RECT 42900.0 290850.0 44100.0 292650.0 ; - RECT 42900.0 281550.0 44100.0 277950.0 ; - RECT 45600.0 290850.0 46500.0 281550.0 ; - RECT 42900.0 281550.0 44100.0 280350.0 ; - RECT 45300.0 281550.0 46500.0 280350.0 ; - RECT 45300.0 281550.0 46500.0 280350.0 ; - RECT 42900.0 281550.0 44100.0 280350.0 ; - RECT 42900.0 290850.0 44100.0 289650.0 ; - RECT 45300.0 290850.0 46500.0 289650.0 ; - RECT 45300.0 290850.0 46500.0 289650.0 ; - RECT 42900.0 290850.0 44100.0 289650.0 ; - RECT 47700.0 280950.0 48900.0 279750.0 ; - RECT 47700.0 290850.0 48900.0 289650.0 ; - RECT 43500.0 286200.0 44700.0 285000.0 ; - RECT 43500.0 286200.0 44700.0 285000.0 ; - RECT 46050.0 286050.0 46950.0 285150.0 ; - RECT 41100.0 278850.0 50700.0 277950.0 ; - RECT 41100.0 292650.0 50700.0 291750.0 ; - RECT 47700.0 304050.0 48900.0 306000.0 ; - RECT 47700.0 292200.0 48900.0 294150.0 ; - RECT 42900.0 293550.0 44100.0 291750.0 ; - RECT 42900.0 302850.0 44100.0 306450.0 ; - RECT 45600.0 293550.0 46500.0 302850.0 ; - RECT 42900.0 302850.0 44100.0 304050.0 ; - RECT 45300.0 302850.0 46500.0 304050.0 ; - RECT 45300.0 302850.0 46500.0 304050.0 ; - RECT 42900.0 302850.0 44100.0 304050.0 ; - RECT 42900.0 293550.0 44100.0 294750.0 ; - RECT 45300.0 293550.0 46500.0 294750.0 ; - RECT 45300.0 293550.0 46500.0 294750.0 ; - RECT 42900.0 293550.0 44100.0 294750.0 ; - RECT 47700.0 303450.0 48900.0 304650.0 ; - RECT 47700.0 293550.0 48900.0 294750.0 ; - RECT 43500.0 298200.0 44700.0 299400.0 ; - RECT 43500.0 298200.0 44700.0 299400.0 ; - RECT 46050.0 298350.0 46950.0 299250.0 ; - RECT 41100.0 305550.0 50700.0 306450.0 ; - RECT 41100.0 291750.0 50700.0 292650.0 ; - RECT 47700.0 307950.0 48900.0 306000.0 ; - RECT 47700.0 319800.0 48900.0 317850.0 ; - RECT 42900.0 318450.0 44100.0 320250.0 ; - RECT 42900.0 309150.0 44100.0 305550.0 ; - RECT 45600.0 318450.0 46500.0 309150.0 ; - RECT 42900.0 309150.0 44100.0 307950.0 ; - RECT 45300.0 309150.0 46500.0 307950.0 ; - RECT 45300.0 309150.0 46500.0 307950.0 ; - RECT 42900.0 309150.0 44100.0 307950.0 ; - RECT 42900.0 318450.0 44100.0 317250.0 ; - RECT 45300.0 318450.0 46500.0 317250.0 ; - RECT 45300.0 318450.0 46500.0 317250.0 ; - RECT 42900.0 318450.0 44100.0 317250.0 ; - RECT 47700.0 308550.0 48900.0 307350.0 ; - RECT 47700.0 318450.0 48900.0 317250.0 ; - RECT 43500.0 313800.0 44700.0 312600.0 ; - RECT 43500.0 313800.0 44700.0 312600.0 ; - RECT 46050.0 313650.0 46950.0 312750.0 ; - RECT 41100.0 306450.0 50700.0 305550.0 ; - RECT 41100.0 320250.0 50700.0 319350.0 ; - RECT 47700.0 331650.0 48900.0 333600.0 ; - RECT 47700.0 319800.0 48900.0 321750.0 ; - RECT 42900.0 321150.0 44100.0 319350.0 ; - RECT 42900.0 330450.0 44100.0 334050.0 ; - RECT 45600.0 321150.0 46500.0 330450.0 ; - RECT 42900.0 330450.0 44100.0 331650.0 ; - RECT 45300.0 330450.0 46500.0 331650.0 ; - RECT 45300.0 330450.0 46500.0 331650.0 ; - RECT 42900.0 330450.0 44100.0 331650.0 ; - RECT 42900.0 321150.0 44100.0 322350.0 ; - RECT 45300.0 321150.0 46500.0 322350.0 ; - RECT 45300.0 321150.0 46500.0 322350.0 ; - RECT 42900.0 321150.0 44100.0 322350.0 ; - RECT 47700.0 331050.0 48900.0 332250.0 ; - RECT 47700.0 321150.0 48900.0 322350.0 ; - RECT 43500.0 325800.0 44700.0 327000.0 ; - RECT 43500.0 325800.0 44700.0 327000.0 ; - RECT 46050.0 325950.0 46950.0 326850.0 ; - RECT 41100.0 333150.0 50700.0 334050.0 ; - RECT 41100.0 319350.0 50700.0 320250.0 ; - RECT 47700.0 335550.0 48900.0 333600.0 ; - RECT 47700.0 347400.0 48900.0 345450.0 ; - RECT 42900.0 346050.0 44100.0 347850.0 ; - RECT 42900.0 336750.0 44100.0 333150.0 ; - RECT 45600.0 346050.0 46500.0 336750.0 ; - RECT 42900.0 336750.0 44100.0 335550.0 ; - RECT 45300.0 336750.0 46500.0 335550.0 ; - RECT 45300.0 336750.0 46500.0 335550.0 ; - RECT 42900.0 336750.0 44100.0 335550.0 ; - RECT 42900.0 346050.0 44100.0 344850.0 ; - RECT 45300.0 346050.0 46500.0 344850.0 ; - RECT 45300.0 346050.0 46500.0 344850.0 ; - RECT 42900.0 346050.0 44100.0 344850.0 ; - RECT 47700.0 336150.0 48900.0 334950.0 ; - RECT 47700.0 346050.0 48900.0 344850.0 ; - RECT 43500.0 341400.0 44700.0 340200.0 ; - RECT 43500.0 341400.0 44700.0 340200.0 ; - RECT 46050.0 341250.0 46950.0 340350.0 ; - RECT 41100.0 334050.0 50700.0 333150.0 ; - RECT 41100.0 347850.0 50700.0 346950.0 ; - RECT 47700.0 359250.0 48900.0 361200.0 ; - RECT 47700.0 347400.0 48900.0 349350.0 ; - RECT 42900.0 348750.0 44100.0 346950.0 ; - RECT 42900.0 358050.0 44100.0 361650.0 ; - RECT 45600.0 348750.0 46500.0 358050.0 ; - RECT 42900.0 358050.0 44100.0 359250.0 ; - RECT 45300.0 358050.0 46500.0 359250.0 ; - RECT 45300.0 358050.0 46500.0 359250.0 ; - RECT 42900.0 358050.0 44100.0 359250.0 ; - RECT 42900.0 348750.0 44100.0 349950.0 ; - RECT 45300.0 348750.0 46500.0 349950.0 ; - RECT 45300.0 348750.0 46500.0 349950.0 ; - RECT 42900.0 348750.0 44100.0 349950.0 ; - RECT 47700.0 358650.0 48900.0 359850.0 ; - RECT 47700.0 348750.0 48900.0 349950.0 ; - RECT 43500.0 353400.0 44700.0 354600.0 ; - RECT 43500.0 353400.0 44700.0 354600.0 ; - RECT 46050.0 353550.0 46950.0 354450.0 ; - RECT 41100.0 360750.0 50700.0 361650.0 ; - RECT 41100.0 346950.0 50700.0 347850.0 ; - RECT 47700.0 363150.0 48900.0 361200.0 ; - RECT 47700.0 375000.0 48900.0 373050.0 ; - RECT 42900.0 373650.0 44100.0 375450.0 ; - RECT 42900.0 364350.0 44100.0 360750.0 ; - RECT 45600.0 373650.0 46500.0 364350.0 ; - RECT 42900.0 364350.0 44100.0 363150.0 ; - RECT 45300.0 364350.0 46500.0 363150.0 ; - RECT 45300.0 364350.0 46500.0 363150.0 ; - RECT 42900.0 364350.0 44100.0 363150.0 ; - RECT 42900.0 373650.0 44100.0 372450.0 ; - RECT 45300.0 373650.0 46500.0 372450.0 ; - RECT 45300.0 373650.0 46500.0 372450.0 ; - RECT 42900.0 373650.0 44100.0 372450.0 ; - RECT 47700.0 363750.0 48900.0 362550.0 ; - RECT 47700.0 373650.0 48900.0 372450.0 ; - RECT 43500.0 369000.0 44700.0 367800.0 ; - RECT 43500.0 369000.0 44700.0 367800.0 ; - RECT 46050.0 368850.0 46950.0 367950.0 ; - RECT 41100.0 361650.0 50700.0 360750.0 ; - RECT 41100.0 375450.0 50700.0 374550.0 ; - RECT 47700.0 386850.0 48900.0 388800.0 ; - RECT 47700.0 375000.0 48900.0 376950.0 ; - RECT 42900.0 376350.0 44100.0 374550.0 ; - RECT 42900.0 385650.0 44100.0 389250.0 ; - RECT 45600.0 376350.0 46500.0 385650.0 ; - RECT 42900.0 385650.0 44100.0 386850.0 ; - RECT 45300.0 385650.0 46500.0 386850.0 ; - RECT 45300.0 385650.0 46500.0 386850.0 ; - RECT 42900.0 385650.0 44100.0 386850.0 ; - RECT 42900.0 376350.0 44100.0 377550.0 ; - RECT 45300.0 376350.0 46500.0 377550.0 ; - RECT 45300.0 376350.0 46500.0 377550.0 ; - RECT 42900.0 376350.0 44100.0 377550.0 ; - RECT 47700.0 386250.0 48900.0 387450.0 ; - RECT 47700.0 376350.0 48900.0 377550.0 ; - RECT 43500.0 381000.0 44700.0 382200.0 ; - RECT 43500.0 381000.0 44700.0 382200.0 ; - RECT 46050.0 381150.0 46950.0 382050.0 ; - RECT 41100.0 388350.0 50700.0 389250.0 ; - RECT 41100.0 374550.0 50700.0 375450.0 ; - RECT 47700.0 390750.0 48900.0 388800.0 ; - RECT 47700.0 402600.0 48900.0 400650.0 ; - RECT 42900.0 401250.0 44100.0 403050.0 ; - RECT 42900.0 391950.0 44100.0 388350.0 ; - RECT 45600.0 401250.0 46500.0 391950.0 ; - RECT 42900.0 391950.0 44100.0 390750.0 ; - RECT 45300.0 391950.0 46500.0 390750.0 ; - RECT 45300.0 391950.0 46500.0 390750.0 ; - RECT 42900.0 391950.0 44100.0 390750.0 ; - RECT 42900.0 401250.0 44100.0 400050.0 ; - RECT 45300.0 401250.0 46500.0 400050.0 ; - RECT 45300.0 401250.0 46500.0 400050.0 ; - RECT 42900.0 401250.0 44100.0 400050.0 ; - RECT 47700.0 391350.0 48900.0 390150.0 ; - RECT 47700.0 401250.0 48900.0 400050.0 ; - RECT 43500.0 396600.0 44700.0 395400.0 ; - RECT 43500.0 396600.0 44700.0 395400.0 ; - RECT 46050.0 396450.0 46950.0 395550.0 ; - RECT 41100.0 389250.0 50700.0 388350.0 ; - RECT 41100.0 403050.0 50700.0 402150.0 ; - RECT 47700.0 414450.0 48900.0 416400.0 ; - RECT 47700.0 402600.0 48900.0 404550.0 ; - RECT 42900.0 403950.0 44100.0 402150.0 ; - RECT 42900.0 413250.0 44100.0 416850.0 ; - RECT 45600.0 403950.0 46500.0 413250.0 ; - RECT 42900.0 413250.0 44100.0 414450.0 ; - RECT 45300.0 413250.0 46500.0 414450.0 ; - RECT 45300.0 413250.0 46500.0 414450.0 ; - RECT 42900.0 413250.0 44100.0 414450.0 ; - RECT 42900.0 403950.0 44100.0 405150.0 ; - RECT 45300.0 403950.0 46500.0 405150.0 ; - RECT 45300.0 403950.0 46500.0 405150.0 ; - RECT 42900.0 403950.0 44100.0 405150.0 ; - RECT 47700.0 413850.0 48900.0 415050.0 ; - RECT 47700.0 403950.0 48900.0 405150.0 ; - RECT 43500.0 408600.0 44700.0 409800.0 ; - RECT 43500.0 408600.0 44700.0 409800.0 ; - RECT 46050.0 408750.0 46950.0 409650.0 ; - RECT 41100.0 415950.0 50700.0 416850.0 ; - RECT 41100.0 402150.0 50700.0 403050.0 ; - RECT 47700.0 418350.0 48900.0 416400.0 ; - RECT 47700.0 430200.0 48900.0 428250.0 ; - RECT 42900.0 428850.0 44100.0 430650.0 ; - RECT 42900.0 419550.0 44100.0 415950.0 ; - RECT 45600.0 428850.0 46500.0 419550.0 ; - RECT 42900.0 419550.0 44100.0 418350.0 ; - RECT 45300.0 419550.0 46500.0 418350.0 ; - RECT 45300.0 419550.0 46500.0 418350.0 ; - RECT 42900.0 419550.0 44100.0 418350.0 ; - RECT 42900.0 428850.0 44100.0 427650.0 ; - RECT 45300.0 428850.0 46500.0 427650.0 ; - RECT 45300.0 428850.0 46500.0 427650.0 ; - RECT 42900.0 428850.0 44100.0 427650.0 ; - RECT 47700.0 418950.0 48900.0 417750.0 ; - RECT 47700.0 428850.0 48900.0 427650.0 ; - RECT 43500.0 424200.0 44700.0 423000.0 ; - RECT 43500.0 424200.0 44700.0 423000.0 ; - RECT 46050.0 424050.0 46950.0 423150.0 ; - RECT 41100.0 416850.0 50700.0 415950.0 ; - RECT 41100.0 430650.0 50700.0 429750.0 ; - RECT 10950.0 105000.0 9750.0 106200.0 ; - RECT 13050.0 119400.0 11850.0 120600.0 ; - RECT 15150.0 132600.0 13950.0 133800.0 ; - RECT 17250.0 147000.0 16050.0 148200.0 ; - RECT 19350.0 160200.0 18150.0 161400.0 ; - RECT 21450.0 174600.0 20250.0 175800.0 ; - RECT 23550.0 187800.0 22350.0 189000.0 ; - RECT 25650.0 202200.0 24450.0 203400.0 ; - RECT 10950.0 216600.0 9750.0 217800.0 ; - RECT 19350.0 213900.0 18150.0 215100.0 ; - RECT 10950.0 228600.0 9750.0 229800.0 ; - RECT 21450.0 231300.0 20250.0 232500.0 ; - RECT 10950.0 244200.0 9750.0 245400.0 ; - RECT 23550.0 241500.0 22350.0 242700.0 ; - RECT 10950.0 256200.0 9750.0 257400.0 ; - RECT 25650.0 258900.0 24450.0 260100.0 ; - RECT 13050.0 271800.0 11850.0 273000.0 ; - RECT 19350.0 269100.0 18150.0 270300.0 ; - RECT 13050.0 283800.0 11850.0 285000.0 ; - RECT 21450.0 286500.0 20250.0 287700.0 ; - RECT 13050.0 299400.0 11850.0 300600.0 ; - RECT 23550.0 296700.0 22350.0 297900.0 ; - RECT 13050.0 311400.0 11850.0 312600.0 ; - RECT 25650.0 314100.0 24450.0 315300.0 ; - RECT 15150.0 327000.0 13950.0 328200.0 ; - RECT 19350.0 324300.0 18150.0 325500.0 ; - RECT 15150.0 339000.0 13950.0 340200.0 ; - RECT 21450.0 341700.0 20250.0 342900.0 ; - RECT 15150.0 354600.0 13950.0 355800.0 ; - RECT 23550.0 351900.0 22350.0 353100.0 ; - RECT 15150.0 366600.0 13950.0 367800.0 ; - RECT 25650.0 369300.0 24450.0 370500.0 ; - RECT 17250.0 382200.0 16050.0 383400.0 ; - RECT 19350.0 379500.0 18150.0 380700.0 ; - RECT 17250.0 394200.0 16050.0 395400.0 ; - RECT 21450.0 396900.0 20250.0 398100.0 ; - RECT 17250.0 409800.0 16050.0 411000.0 ; - RECT 23550.0 407100.0 22350.0 408300.0 ; - RECT 17250.0 421800.0 16050.0 423000.0 ; - RECT 25650.0 424500.0 24450.0 425700.0 ; - RECT 46050.0 215550.0 46950.0 216450.0 ; - RECT 46050.0 229950.0 46950.0 230850.0 ; - RECT 46050.0 243150.0 46950.0 244050.0 ; - RECT 46050.0 257550.0 46950.0 258450.0 ; - RECT 46050.0 270750.0 46950.0 271650.0 ; - RECT 46050.0 285150.0 46950.0 286050.0 ; - RECT 46050.0 298350.0 46950.0 299250.0 ; - RECT 46050.0 312750.0 46950.0 313650.0 ; - RECT 46050.0 325950.0 46950.0 326850.0 ; - RECT 46050.0 340350.0 46950.0 341250.0 ; - RECT 46050.0 353550.0 46950.0 354450.0 ; - RECT 46050.0 367950.0 46950.0 368850.0 ; - RECT 46050.0 381150.0 46950.0 382050.0 ; - RECT 46050.0 395550.0 46950.0 396450.0 ; - RECT 46050.0 408750.0 46950.0 409650.0 ; - RECT 46050.0 423150.0 46950.0 424050.0 ; - RECT 9900.0 112350.0 81300.0 113250.0 ; - RECT 9900.0 139950.0 81300.0 140850.0 ; - RECT 9900.0 167550.0 81300.0 168450.0 ; - RECT 9900.0 195150.0 81300.0 196050.0 ; - RECT 9900.0 222750.0 81300.0 223650.0 ; - RECT 9900.0 250350.0 81300.0 251250.0 ; - RECT 9900.0 277950.0 81300.0 278850.0 ; - RECT 9900.0 305550.0 81300.0 306450.0 ; - RECT 9900.0 333150.0 81300.0 334050.0 ; - RECT 9900.0 360750.0 81300.0 361650.0 ; - RECT 9900.0 388350.0 81300.0 389250.0 ; - RECT 9900.0 415950.0 81300.0 416850.0 ; - RECT 9900.0 98550.0 81300.0 99450.0 ; - RECT 9900.0 126150.0 81300.0 127050.0 ; - RECT 9900.0 153750.0 81300.0 154650.0 ; - RECT 9900.0 181350.0 81300.0 182250.0 ; - RECT 9900.0 208950.0 81300.0 209850.0 ; - RECT 9900.0 236550.0 81300.0 237450.0 ; - RECT 9900.0 264150.0 81300.0 265050.0 ; - RECT 9900.0 291750.0 81300.0 292650.0 ; - RECT 9900.0 319350.0 81300.0 320250.0 ; - RECT 9900.0 346950.0 81300.0 347850.0 ; - RECT 9900.0 374550.0 81300.0 375450.0 ; - RECT 9900.0 402150.0 81300.0 403050.0 ; - RECT 9900.0 429750.0 81300.0 430650.0 ; - RECT 53850.0 215550.0 59400.0 216450.0 ; - RECT 61950.0 216750.0 62850.0 217650.0 ; - RECT 61950.0 215550.0 62850.0 216450.0 ; - RECT 61950.0 216450.0 62850.0 217200.0 ; - RECT 62400.0 216750.0 69000.0 217650.0 ; - RECT 69000.0 216750.0 70200.0 217650.0 ; - RECT 78450.0 216750.0 79350.0 217650.0 ; - RECT 78450.0 215550.0 79350.0 216450.0 ; - RECT 74400.0 216750.0 78900.0 217650.0 ; - RECT 78450.0 216000.0 79350.0 217200.0 ; - RECT 78900.0 215550.0 83400.0 216450.0 ; - RECT 53850.0 229950.0 59400.0 230850.0 ; - RECT 61950.0 228750.0 62850.0 229650.0 ; - RECT 61950.0 229950.0 62850.0 230850.0 ; - RECT 61950.0 229200.0 62850.0 230850.0 ; - RECT 62400.0 228750.0 69000.0 229650.0 ; - RECT 69000.0 228750.0 70200.0 229650.0 ; - RECT 78450.0 228750.0 79350.0 229650.0 ; - RECT 78450.0 229950.0 79350.0 230850.0 ; - RECT 74400.0 228750.0 78900.0 229650.0 ; - RECT 78450.0 229200.0 79350.0 230400.0 ; - RECT 78900.0 229950.0 83400.0 230850.0 ; - RECT 53850.0 243150.0 59400.0 244050.0 ; - RECT 61950.0 244350.0 62850.0 245250.0 ; - RECT 61950.0 243150.0 62850.0 244050.0 ; - RECT 61950.0 244050.0 62850.0 244800.0 ; - RECT 62400.0 244350.0 69000.0 245250.0 ; - RECT 69000.0 244350.0 70200.0 245250.0 ; - RECT 78450.0 244350.0 79350.0 245250.0 ; - RECT 78450.0 243150.0 79350.0 244050.0 ; - RECT 74400.0 244350.0 78900.0 245250.0 ; - RECT 78450.0 243600.0 79350.0 244800.0 ; - RECT 78900.0 243150.0 83400.0 244050.0 ; - RECT 53850.0 257550.0 59400.0 258450.0 ; - RECT 61950.0 256350.0 62850.0 257250.0 ; - RECT 61950.0 257550.0 62850.0 258450.0 ; - RECT 61950.0 256800.0 62850.0 258450.0 ; - RECT 62400.0 256350.0 69000.0 257250.0 ; - RECT 69000.0 256350.0 70200.0 257250.0 ; - RECT 78450.0 256350.0 79350.0 257250.0 ; - RECT 78450.0 257550.0 79350.0 258450.0 ; - RECT 74400.0 256350.0 78900.0 257250.0 ; - RECT 78450.0 256800.0 79350.0 258000.0 ; - RECT 78900.0 257550.0 83400.0 258450.0 ; - RECT 53850.0 270750.0 59400.0 271650.0 ; - RECT 61950.0 271950.0 62850.0 272850.0 ; - RECT 61950.0 270750.0 62850.0 271650.0 ; - RECT 61950.0 271650.0 62850.0 272400.0 ; - RECT 62400.0 271950.0 69000.0 272850.0 ; - RECT 69000.0 271950.0 70200.0 272850.0 ; - RECT 78450.0 271950.0 79350.0 272850.0 ; - RECT 78450.0 270750.0 79350.0 271650.0 ; - RECT 74400.0 271950.0 78900.0 272850.0 ; - RECT 78450.0 271200.0 79350.0 272400.0 ; - RECT 78900.0 270750.0 83400.0 271650.0 ; - RECT 53850.0 285150.0 59400.0 286050.0 ; - RECT 61950.0 283950.0 62850.0 284850.0 ; - RECT 61950.0 285150.0 62850.0 286050.0 ; - RECT 61950.0 284400.0 62850.0 286050.0 ; - RECT 62400.0 283950.0 69000.0 284850.0 ; - RECT 69000.0 283950.0 70200.0 284850.0 ; - RECT 78450.0 283950.0 79350.0 284850.0 ; - RECT 78450.0 285150.0 79350.0 286050.0 ; - RECT 74400.0 283950.0 78900.0 284850.0 ; - RECT 78450.0 284400.0 79350.0 285600.0 ; - RECT 78900.0 285150.0 83400.0 286050.0 ; - RECT 53850.0 298350.0 59400.0 299250.0 ; - RECT 61950.0 299550.0 62850.0 300450.0 ; - RECT 61950.0 298350.0 62850.0 299250.0 ; - RECT 61950.0 299250.0 62850.0 300000.0 ; - RECT 62400.0 299550.0 69000.0 300450.0 ; - RECT 69000.0 299550.0 70200.0 300450.0 ; - RECT 78450.0 299550.0 79350.0 300450.0 ; - RECT 78450.0 298350.0 79350.0 299250.0 ; - RECT 74400.0 299550.0 78900.0 300450.0 ; - RECT 78450.0 298800.0 79350.0 300000.0 ; - RECT 78900.0 298350.0 83400.0 299250.0 ; - RECT 53850.0 312750.0 59400.0 313650.0 ; - RECT 61950.0 311550.0 62850.0 312450.0 ; - RECT 61950.0 312750.0 62850.0 313650.0 ; - RECT 61950.0 312000.0 62850.0 313650.0 ; - RECT 62400.0 311550.0 69000.0 312450.0 ; - RECT 69000.0 311550.0 70200.0 312450.0 ; - RECT 78450.0 311550.0 79350.0 312450.0 ; - RECT 78450.0 312750.0 79350.0 313650.0 ; - RECT 74400.0 311550.0 78900.0 312450.0 ; - RECT 78450.0 312000.0 79350.0 313200.0 ; - RECT 78900.0 312750.0 83400.0 313650.0 ; - RECT 53850.0 325950.0 59400.0 326850.0 ; - RECT 61950.0 327150.0 62850.0 328050.0 ; - RECT 61950.0 325950.0 62850.0 326850.0 ; - RECT 61950.0 326850.0 62850.0 327600.0 ; - RECT 62400.0 327150.0 69000.0 328050.0 ; - RECT 69000.0 327150.0 70200.0 328050.0 ; - RECT 78450.0 327150.0 79350.0 328050.0 ; - RECT 78450.0 325950.0 79350.0 326850.0 ; - RECT 74400.0 327150.0 78900.0 328050.0 ; - RECT 78450.0 326400.0 79350.0 327600.0 ; - RECT 78900.0 325950.0 83400.0 326850.0 ; - RECT 53850.0 340350.0 59400.0 341250.0 ; - RECT 61950.0 339150.0 62850.0 340050.0 ; - RECT 61950.0 340350.0 62850.0 341250.0 ; - RECT 61950.0 339600.0 62850.0 341250.0 ; - RECT 62400.0 339150.0 69000.0 340050.0 ; - RECT 69000.0 339150.0 70200.0 340050.0 ; - RECT 78450.0 339150.0 79350.0 340050.0 ; - RECT 78450.0 340350.0 79350.0 341250.0 ; - RECT 74400.0 339150.0 78900.0 340050.0 ; - RECT 78450.0 339600.0 79350.0 340800.0 ; - RECT 78900.0 340350.0 83400.0 341250.0 ; - RECT 53850.0 353550.0 59400.0 354450.0 ; - RECT 61950.0 354750.0 62850.0 355650.0 ; - RECT 61950.0 353550.0 62850.0 354450.0 ; - RECT 61950.0 354450.0 62850.0 355200.0 ; - RECT 62400.0 354750.0 69000.0 355650.0 ; - RECT 69000.0 354750.0 70200.0 355650.0 ; - RECT 78450.0 354750.0 79350.0 355650.0 ; - RECT 78450.0 353550.0 79350.0 354450.0 ; - RECT 74400.0 354750.0 78900.0 355650.0 ; - RECT 78450.0 354000.0 79350.0 355200.0 ; - RECT 78900.0 353550.0 83400.0 354450.0 ; - RECT 53850.0 367950.0 59400.0 368850.0 ; - RECT 61950.0 366750.0 62850.0 367650.0 ; - RECT 61950.0 367950.0 62850.0 368850.0 ; - RECT 61950.0 367200.0 62850.0 368850.0 ; - RECT 62400.0 366750.0 69000.0 367650.0 ; - RECT 69000.0 366750.0 70200.0 367650.0 ; - RECT 78450.0 366750.0 79350.0 367650.0 ; - RECT 78450.0 367950.0 79350.0 368850.0 ; - RECT 74400.0 366750.0 78900.0 367650.0 ; - RECT 78450.0 367200.0 79350.0 368400.0 ; - RECT 78900.0 367950.0 83400.0 368850.0 ; - RECT 53850.0 381150.0 59400.0 382050.0 ; - RECT 61950.0 382350.0 62850.0 383250.0 ; - RECT 61950.0 381150.0 62850.0 382050.0 ; - RECT 61950.0 382050.0 62850.0 382800.0 ; - RECT 62400.0 382350.0 69000.0 383250.0 ; - RECT 69000.0 382350.0 70200.0 383250.0 ; - RECT 78450.0 382350.0 79350.0 383250.0 ; - RECT 78450.0 381150.0 79350.0 382050.0 ; - RECT 74400.0 382350.0 78900.0 383250.0 ; - RECT 78450.0 381600.0 79350.0 382800.0 ; - RECT 78900.0 381150.0 83400.0 382050.0 ; - RECT 53850.0 395550.0 59400.0 396450.0 ; - RECT 61950.0 394350.0 62850.0 395250.0 ; - RECT 61950.0 395550.0 62850.0 396450.0 ; - RECT 61950.0 394800.0 62850.0 396450.0 ; - RECT 62400.0 394350.0 69000.0 395250.0 ; - RECT 69000.0 394350.0 70200.0 395250.0 ; - RECT 78450.0 394350.0 79350.0 395250.0 ; - RECT 78450.0 395550.0 79350.0 396450.0 ; - RECT 74400.0 394350.0 78900.0 395250.0 ; - RECT 78450.0 394800.0 79350.0 396000.0 ; - RECT 78900.0 395550.0 83400.0 396450.0 ; - RECT 53850.0 408750.0 59400.0 409650.0 ; - RECT 61950.0 409950.0 62850.0 410850.0 ; - RECT 61950.0 408750.0 62850.0 409650.0 ; - RECT 61950.0 409650.0 62850.0 410400.0 ; - RECT 62400.0 409950.0 69000.0 410850.0 ; - RECT 69000.0 409950.0 70200.0 410850.0 ; - RECT 78450.0 409950.0 79350.0 410850.0 ; - RECT 78450.0 408750.0 79350.0 409650.0 ; - RECT 74400.0 409950.0 78900.0 410850.0 ; - RECT 78450.0 409200.0 79350.0 410400.0 ; - RECT 78900.0 408750.0 83400.0 409650.0 ; - RECT 53850.0 423150.0 59400.0 424050.0 ; - RECT 61950.0 421950.0 62850.0 422850.0 ; - RECT 61950.0 423150.0 62850.0 424050.0 ; - RECT 61950.0 422400.0 62850.0 424050.0 ; - RECT 62400.0 421950.0 69000.0 422850.0 ; - RECT 69000.0 421950.0 70200.0 422850.0 ; - RECT 78450.0 421950.0 79350.0 422850.0 ; - RECT 78450.0 423150.0 79350.0 424050.0 ; - RECT 74400.0 421950.0 78900.0 422850.0 ; - RECT 78450.0 422400.0 79350.0 423600.0 ; - RECT 78900.0 423150.0 83400.0 424050.0 ; - RECT 63600.0 221250.0 64800.0 223200.0 ; - RECT 63600.0 209400.0 64800.0 211350.0 ; - RECT 58800.0 210750.0 60000.0 208950.0 ; - RECT 58800.0 220050.0 60000.0 223650.0 ; - RECT 61500.0 210750.0 62400.0 220050.0 ; - RECT 58800.0 220050.0 60000.0 221250.0 ; - RECT 61200.0 220050.0 62400.0 221250.0 ; - RECT 61200.0 220050.0 62400.0 221250.0 ; - RECT 58800.0 220050.0 60000.0 221250.0 ; - RECT 58800.0 210750.0 60000.0 211950.0 ; - RECT 61200.0 210750.0 62400.0 211950.0 ; - RECT 61200.0 210750.0 62400.0 211950.0 ; - RECT 58800.0 210750.0 60000.0 211950.0 ; - RECT 63600.0 220650.0 64800.0 221850.0 ; - RECT 63600.0 210750.0 64800.0 211950.0 ; - RECT 59400.0 215400.0 60600.0 216600.0 ; - RECT 59400.0 215400.0 60600.0 216600.0 ; - RECT 61950.0 215550.0 62850.0 216450.0 ; - RECT 57000.0 222750.0 66600.0 223650.0 ; - RECT 57000.0 208950.0 66600.0 209850.0 ; - RECT 68400.0 211350.0 69600.0 208950.0 ; - RECT 68400.0 220050.0 69600.0 223650.0 ; - RECT 73200.0 220050.0 74400.0 223650.0 ; - RECT 75600.0 221250.0 76800.0 223200.0 ; - RECT 75600.0 209400.0 76800.0 211350.0 ; - RECT 68400.0 220050.0 69600.0 221250.0 ; - RECT 70800.0 220050.0 72000.0 221250.0 ; - RECT 70800.0 220050.0 72000.0 221250.0 ; - RECT 68400.0 220050.0 69600.0 221250.0 ; - RECT 70800.0 220050.0 72000.0 221250.0 ; - RECT 73200.0 220050.0 74400.0 221250.0 ; - RECT 73200.0 220050.0 74400.0 221250.0 ; - RECT 70800.0 220050.0 72000.0 221250.0 ; - RECT 68400.0 211350.0 69600.0 212550.0 ; - RECT 70800.0 211350.0 72000.0 212550.0 ; - RECT 70800.0 211350.0 72000.0 212550.0 ; - RECT 68400.0 211350.0 69600.0 212550.0 ; - RECT 70800.0 211350.0 72000.0 212550.0 ; - RECT 73200.0 211350.0 74400.0 212550.0 ; - RECT 73200.0 211350.0 74400.0 212550.0 ; - RECT 70800.0 211350.0 72000.0 212550.0 ; - RECT 75600.0 220650.0 76800.0 221850.0 ; - RECT 75600.0 210750.0 76800.0 211950.0 ; - RECT 73200.0 213900.0 72000.0 215100.0 ; - RECT 70200.0 216600.0 69000.0 217800.0 ; - RECT 70800.0 220050.0 72000.0 221250.0 ; - RECT 73200.0 211350.0 74400.0 212550.0 ; - RECT 74400.0 216600.0 73200.0 217800.0 ; - RECT 69000.0 216600.0 70200.0 217800.0 ; - RECT 72000.0 213900.0 73200.0 215100.0 ; - RECT 73200.0 216600.0 74400.0 217800.0 ; - RECT 66600.0 222750.0 81000.0 223650.0 ; - RECT 66600.0 208950.0 81000.0 209850.0 ; - RECT 87600.0 221250.0 88800.0 223200.0 ; - RECT 87600.0 209400.0 88800.0 211350.0 ; - RECT 82800.0 210750.0 84000.0 208950.0 ; - RECT 82800.0 220050.0 84000.0 223650.0 ; - RECT 85500.0 210750.0 86400.0 220050.0 ; - RECT 82800.0 220050.0 84000.0 221250.0 ; - RECT 85200.0 220050.0 86400.0 221250.0 ; - RECT 85200.0 220050.0 86400.0 221250.0 ; - RECT 82800.0 220050.0 84000.0 221250.0 ; - RECT 82800.0 210750.0 84000.0 211950.0 ; - RECT 85200.0 210750.0 86400.0 211950.0 ; - RECT 85200.0 210750.0 86400.0 211950.0 ; - RECT 82800.0 210750.0 84000.0 211950.0 ; - RECT 87600.0 220650.0 88800.0 221850.0 ; - RECT 87600.0 210750.0 88800.0 211950.0 ; - RECT 83400.0 215400.0 84600.0 216600.0 ; - RECT 83400.0 215400.0 84600.0 216600.0 ; - RECT 85950.0 215550.0 86850.0 216450.0 ; - RECT 81000.0 222750.0 90600.0 223650.0 ; - RECT 81000.0 208950.0 90600.0 209850.0 ; - RECT 53250.0 215400.0 54450.0 216600.0 ; - RECT 55200.0 213000.0 56400.0 214200.0 ; - RECT 72000.0 213900.0 70800.0 215100.0 ; - RECT 63600.0 225150.0 64800.0 223200.0 ; - RECT 63600.0 237000.0 64800.0 235050.0 ; - RECT 58800.0 235650.0 60000.0 237450.0 ; - RECT 58800.0 226350.0 60000.0 222750.0 ; - RECT 61500.0 235650.0 62400.0 226350.0 ; - RECT 58800.0 226350.0 60000.0 225150.0 ; - RECT 61200.0 226350.0 62400.0 225150.0 ; - RECT 61200.0 226350.0 62400.0 225150.0 ; - RECT 58800.0 226350.0 60000.0 225150.0 ; - RECT 58800.0 235650.0 60000.0 234450.0 ; - RECT 61200.0 235650.0 62400.0 234450.0 ; - RECT 61200.0 235650.0 62400.0 234450.0 ; - RECT 58800.0 235650.0 60000.0 234450.0 ; - RECT 63600.0 225750.0 64800.0 224550.0 ; - RECT 63600.0 235650.0 64800.0 234450.0 ; - RECT 59400.0 231000.0 60600.0 229800.0 ; - RECT 59400.0 231000.0 60600.0 229800.0 ; - RECT 61950.0 230850.0 62850.0 229950.0 ; - RECT 57000.0 223650.0 66600.0 222750.0 ; - RECT 57000.0 237450.0 66600.0 236550.0 ; - RECT 68400.0 235050.0 69600.0 237450.0 ; - RECT 68400.0 226350.0 69600.0 222750.0 ; - RECT 73200.0 226350.0 74400.0 222750.0 ; - RECT 75600.0 225150.0 76800.0 223200.0 ; - RECT 75600.0 237000.0 76800.0 235050.0 ; - RECT 68400.0 226350.0 69600.0 225150.0 ; - RECT 70800.0 226350.0 72000.0 225150.0 ; - RECT 70800.0 226350.0 72000.0 225150.0 ; - RECT 68400.0 226350.0 69600.0 225150.0 ; - RECT 70800.0 226350.0 72000.0 225150.0 ; - RECT 73200.0 226350.0 74400.0 225150.0 ; - RECT 73200.0 226350.0 74400.0 225150.0 ; - RECT 70800.0 226350.0 72000.0 225150.0 ; - RECT 68400.0 235050.0 69600.0 233850.0 ; - RECT 70800.0 235050.0 72000.0 233850.0 ; - RECT 70800.0 235050.0 72000.0 233850.0 ; - RECT 68400.0 235050.0 69600.0 233850.0 ; - RECT 70800.0 235050.0 72000.0 233850.0 ; - RECT 73200.0 235050.0 74400.0 233850.0 ; - RECT 73200.0 235050.0 74400.0 233850.0 ; - RECT 70800.0 235050.0 72000.0 233850.0 ; - RECT 75600.0 225750.0 76800.0 224550.0 ; - RECT 75600.0 235650.0 76800.0 234450.0 ; - RECT 73200.0 232500.0 72000.0 231300.0 ; - RECT 70200.0 229800.0 69000.0 228600.0 ; - RECT 70800.0 226350.0 72000.0 225150.0 ; - RECT 73200.0 235050.0 74400.0 233850.0 ; - RECT 74400.0 229800.0 73200.0 228600.0 ; - RECT 69000.0 229800.0 70200.0 228600.0 ; - RECT 72000.0 232500.0 73200.0 231300.0 ; - RECT 73200.0 229800.0 74400.0 228600.0 ; - RECT 66600.0 223650.0 81000.0 222750.0 ; - RECT 66600.0 237450.0 81000.0 236550.0 ; - RECT 87600.0 225150.0 88800.0 223200.0 ; - RECT 87600.0 237000.0 88800.0 235050.0 ; - RECT 82800.0 235650.0 84000.0 237450.0 ; - RECT 82800.0 226350.0 84000.0 222750.0 ; - RECT 85500.0 235650.0 86400.0 226350.0 ; - RECT 82800.0 226350.0 84000.0 225150.0 ; - RECT 85200.0 226350.0 86400.0 225150.0 ; - RECT 85200.0 226350.0 86400.0 225150.0 ; - RECT 82800.0 226350.0 84000.0 225150.0 ; - RECT 82800.0 235650.0 84000.0 234450.0 ; - RECT 85200.0 235650.0 86400.0 234450.0 ; - RECT 85200.0 235650.0 86400.0 234450.0 ; - RECT 82800.0 235650.0 84000.0 234450.0 ; - RECT 87600.0 225750.0 88800.0 224550.0 ; - RECT 87600.0 235650.0 88800.0 234450.0 ; - RECT 83400.0 231000.0 84600.0 229800.0 ; - RECT 83400.0 231000.0 84600.0 229800.0 ; - RECT 85950.0 230850.0 86850.0 229950.0 ; - RECT 81000.0 223650.0 90600.0 222750.0 ; - RECT 81000.0 237450.0 90600.0 236550.0 ; - RECT 53250.0 229800.0 54450.0 231000.0 ; - RECT 55200.0 232200.0 56400.0 233400.0 ; - RECT 72000.0 231300.0 70800.0 232500.0 ; - RECT 63600.0 248850.0 64800.0 250800.0 ; - RECT 63600.0 237000.0 64800.0 238950.0 ; - RECT 58800.0 238350.0 60000.0 236550.0 ; - RECT 58800.0 247650.0 60000.0 251250.0 ; - RECT 61500.0 238350.0 62400.0 247650.0 ; - RECT 58800.0 247650.0 60000.0 248850.0 ; - RECT 61200.0 247650.0 62400.0 248850.0 ; - RECT 61200.0 247650.0 62400.0 248850.0 ; - RECT 58800.0 247650.0 60000.0 248850.0 ; - RECT 58800.0 238350.0 60000.0 239550.0 ; - RECT 61200.0 238350.0 62400.0 239550.0 ; - RECT 61200.0 238350.0 62400.0 239550.0 ; - RECT 58800.0 238350.0 60000.0 239550.0 ; - RECT 63600.0 248250.0 64800.0 249450.0 ; - RECT 63600.0 238350.0 64800.0 239550.0 ; - RECT 59400.0 243000.0 60600.0 244200.0 ; - RECT 59400.0 243000.0 60600.0 244200.0 ; - RECT 61950.0 243150.0 62850.0 244050.0 ; - RECT 57000.0 250350.0 66600.0 251250.0 ; - RECT 57000.0 236550.0 66600.0 237450.0 ; - RECT 68400.0 238950.0 69600.0 236550.0 ; - RECT 68400.0 247650.0 69600.0 251250.0 ; - RECT 73200.0 247650.0 74400.0 251250.0 ; - RECT 75600.0 248850.0 76800.0 250800.0 ; - RECT 75600.0 237000.0 76800.0 238950.0 ; - RECT 68400.0 247650.0 69600.0 248850.0 ; - RECT 70800.0 247650.0 72000.0 248850.0 ; - RECT 70800.0 247650.0 72000.0 248850.0 ; - RECT 68400.0 247650.0 69600.0 248850.0 ; - RECT 70800.0 247650.0 72000.0 248850.0 ; - RECT 73200.0 247650.0 74400.0 248850.0 ; - RECT 73200.0 247650.0 74400.0 248850.0 ; - RECT 70800.0 247650.0 72000.0 248850.0 ; - RECT 68400.0 238950.0 69600.0 240150.0 ; - RECT 70800.0 238950.0 72000.0 240150.0 ; - RECT 70800.0 238950.0 72000.0 240150.0 ; - RECT 68400.0 238950.0 69600.0 240150.0 ; - RECT 70800.0 238950.0 72000.0 240150.0 ; - RECT 73200.0 238950.0 74400.0 240150.0 ; - RECT 73200.0 238950.0 74400.0 240150.0 ; - RECT 70800.0 238950.0 72000.0 240150.0 ; - RECT 75600.0 248250.0 76800.0 249450.0 ; - RECT 75600.0 238350.0 76800.0 239550.0 ; - RECT 73200.0 241500.0 72000.0 242700.0 ; - RECT 70200.0 244200.0 69000.0 245400.0 ; - RECT 70800.0 247650.0 72000.0 248850.0 ; - RECT 73200.0 238950.0 74400.0 240150.0 ; - RECT 74400.0 244200.0 73200.0 245400.0 ; - RECT 69000.0 244200.0 70200.0 245400.0 ; - RECT 72000.0 241500.0 73200.0 242700.0 ; - RECT 73200.0 244200.0 74400.0 245400.0 ; - RECT 66600.0 250350.0 81000.0 251250.0 ; - RECT 66600.0 236550.0 81000.0 237450.0 ; - RECT 87600.0 248850.0 88800.0 250800.0 ; - RECT 87600.0 237000.0 88800.0 238950.0 ; - RECT 82800.0 238350.0 84000.0 236550.0 ; - RECT 82800.0 247650.0 84000.0 251250.0 ; - RECT 85500.0 238350.0 86400.0 247650.0 ; - RECT 82800.0 247650.0 84000.0 248850.0 ; - RECT 85200.0 247650.0 86400.0 248850.0 ; - RECT 85200.0 247650.0 86400.0 248850.0 ; - RECT 82800.0 247650.0 84000.0 248850.0 ; - RECT 82800.0 238350.0 84000.0 239550.0 ; - RECT 85200.0 238350.0 86400.0 239550.0 ; - RECT 85200.0 238350.0 86400.0 239550.0 ; - RECT 82800.0 238350.0 84000.0 239550.0 ; - RECT 87600.0 248250.0 88800.0 249450.0 ; - RECT 87600.0 238350.0 88800.0 239550.0 ; - RECT 83400.0 243000.0 84600.0 244200.0 ; - RECT 83400.0 243000.0 84600.0 244200.0 ; - RECT 85950.0 243150.0 86850.0 244050.0 ; - RECT 81000.0 250350.0 90600.0 251250.0 ; - RECT 81000.0 236550.0 90600.0 237450.0 ; - RECT 53250.0 243000.0 54450.0 244200.0 ; - RECT 55200.0 240600.0 56400.0 241800.0 ; - RECT 72000.0 241500.0 70800.0 242700.0 ; - RECT 63600.0 252750.0 64800.0 250800.0 ; - RECT 63600.0 264600.0 64800.0 262650.0 ; - RECT 58800.0 263250.0 60000.0 265050.0 ; - RECT 58800.0 253950.0 60000.0 250350.0 ; - RECT 61500.0 263250.0 62400.0 253950.0 ; - RECT 58800.0 253950.0 60000.0 252750.0 ; - RECT 61200.0 253950.0 62400.0 252750.0 ; - RECT 61200.0 253950.0 62400.0 252750.0 ; - RECT 58800.0 253950.0 60000.0 252750.0 ; - RECT 58800.0 263250.0 60000.0 262050.0 ; - RECT 61200.0 263250.0 62400.0 262050.0 ; - RECT 61200.0 263250.0 62400.0 262050.0 ; - RECT 58800.0 263250.0 60000.0 262050.0 ; - RECT 63600.0 253350.0 64800.0 252150.0 ; - RECT 63600.0 263250.0 64800.0 262050.0 ; - RECT 59400.0 258600.0 60600.0 257400.0 ; - RECT 59400.0 258600.0 60600.0 257400.0 ; - RECT 61950.0 258450.0 62850.0 257550.0 ; - RECT 57000.0 251250.0 66600.0 250350.0 ; - RECT 57000.0 265050.0 66600.0 264150.0 ; - RECT 68400.0 262650.0 69600.0 265050.0 ; - RECT 68400.0 253950.0 69600.0 250350.0 ; - RECT 73200.0 253950.0 74400.0 250350.0 ; - RECT 75600.0 252750.0 76800.0 250800.0 ; - RECT 75600.0 264600.0 76800.0 262650.0 ; - RECT 68400.0 253950.0 69600.0 252750.0 ; - RECT 70800.0 253950.0 72000.0 252750.0 ; - RECT 70800.0 253950.0 72000.0 252750.0 ; - RECT 68400.0 253950.0 69600.0 252750.0 ; - RECT 70800.0 253950.0 72000.0 252750.0 ; - RECT 73200.0 253950.0 74400.0 252750.0 ; - RECT 73200.0 253950.0 74400.0 252750.0 ; - RECT 70800.0 253950.0 72000.0 252750.0 ; - RECT 68400.0 262650.0 69600.0 261450.0 ; - RECT 70800.0 262650.0 72000.0 261450.0 ; - RECT 70800.0 262650.0 72000.0 261450.0 ; - RECT 68400.0 262650.0 69600.0 261450.0 ; - RECT 70800.0 262650.0 72000.0 261450.0 ; - RECT 73200.0 262650.0 74400.0 261450.0 ; - RECT 73200.0 262650.0 74400.0 261450.0 ; - RECT 70800.0 262650.0 72000.0 261450.0 ; - RECT 75600.0 253350.0 76800.0 252150.0 ; - RECT 75600.0 263250.0 76800.0 262050.0 ; - RECT 73200.0 260100.0 72000.0 258900.0 ; - RECT 70200.0 257400.0 69000.0 256200.0 ; - RECT 70800.0 253950.0 72000.0 252750.0 ; - RECT 73200.0 262650.0 74400.0 261450.0 ; - RECT 74400.0 257400.0 73200.0 256200.0 ; - RECT 69000.0 257400.0 70200.0 256200.0 ; - RECT 72000.0 260100.0 73200.0 258900.0 ; - RECT 73200.0 257400.0 74400.0 256200.0 ; - RECT 66600.0 251250.0 81000.0 250350.0 ; - RECT 66600.0 265050.0 81000.0 264150.0 ; - RECT 87600.0 252750.0 88800.0 250800.0 ; - RECT 87600.0 264600.0 88800.0 262650.0 ; - RECT 82800.0 263250.0 84000.0 265050.0 ; - RECT 82800.0 253950.0 84000.0 250350.0 ; - RECT 85500.0 263250.0 86400.0 253950.0 ; - RECT 82800.0 253950.0 84000.0 252750.0 ; - RECT 85200.0 253950.0 86400.0 252750.0 ; - RECT 85200.0 253950.0 86400.0 252750.0 ; - RECT 82800.0 253950.0 84000.0 252750.0 ; - RECT 82800.0 263250.0 84000.0 262050.0 ; - RECT 85200.0 263250.0 86400.0 262050.0 ; - RECT 85200.0 263250.0 86400.0 262050.0 ; - RECT 82800.0 263250.0 84000.0 262050.0 ; - RECT 87600.0 253350.0 88800.0 252150.0 ; - RECT 87600.0 263250.0 88800.0 262050.0 ; - RECT 83400.0 258600.0 84600.0 257400.0 ; - RECT 83400.0 258600.0 84600.0 257400.0 ; - RECT 85950.0 258450.0 86850.0 257550.0 ; - RECT 81000.0 251250.0 90600.0 250350.0 ; - RECT 81000.0 265050.0 90600.0 264150.0 ; - RECT 53250.0 257400.0 54450.0 258600.0 ; - RECT 55200.0 259800.0 56400.0 261000.0 ; - RECT 72000.0 258900.0 70800.0 260100.0 ; - RECT 63600.0 276450.0 64800.0 278400.0 ; - RECT 63600.0 264600.0 64800.0 266550.0 ; - RECT 58800.0 265950.0 60000.0 264150.0 ; - RECT 58800.0 275250.0 60000.0 278850.0 ; - RECT 61500.0 265950.0 62400.0 275250.0 ; - RECT 58800.0 275250.0 60000.0 276450.0 ; - RECT 61200.0 275250.0 62400.0 276450.0 ; - RECT 61200.0 275250.0 62400.0 276450.0 ; - RECT 58800.0 275250.0 60000.0 276450.0 ; - RECT 58800.0 265950.0 60000.0 267150.0 ; - RECT 61200.0 265950.0 62400.0 267150.0 ; - RECT 61200.0 265950.0 62400.0 267150.0 ; - RECT 58800.0 265950.0 60000.0 267150.0 ; - RECT 63600.0 275850.0 64800.0 277050.0 ; - RECT 63600.0 265950.0 64800.0 267150.0 ; - RECT 59400.0 270600.0 60600.0 271800.0 ; - RECT 59400.0 270600.0 60600.0 271800.0 ; - RECT 61950.0 270750.0 62850.0 271650.0 ; - RECT 57000.0 277950.0 66600.0 278850.0 ; - RECT 57000.0 264150.0 66600.0 265050.0 ; - RECT 68400.0 266550.0 69600.0 264150.0 ; - RECT 68400.0 275250.0 69600.0 278850.0 ; - RECT 73200.0 275250.0 74400.0 278850.0 ; - RECT 75600.0 276450.0 76800.0 278400.0 ; - RECT 75600.0 264600.0 76800.0 266550.0 ; - RECT 68400.0 275250.0 69600.0 276450.0 ; - RECT 70800.0 275250.0 72000.0 276450.0 ; - RECT 70800.0 275250.0 72000.0 276450.0 ; - RECT 68400.0 275250.0 69600.0 276450.0 ; - RECT 70800.0 275250.0 72000.0 276450.0 ; - RECT 73200.0 275250.0 74400.0 276450.0 ; - RECT 73200.0 275250.0 74400.0 276450.0 ; - RECT 70800.0 275250.0 72000.0 276450.0 ; - RECT 68400.0 266550.0 69600.0 267750.0 ; - RECT 70800.0 266550.0 72000.0 267750.0 ; - RECT 70800.0 266550.0 72000.0 267750.0 ; - RECT 68400.0 266550.0 69600.0 267750.0 ; - RECT 70800.0 266550.0 72000.0 267750.0 ; - RECT 73200.0 266550.0 74400.0 267750.0 ; - RECT 73200.0 266550.0 74400.0 267750.0 ; - RECT 70800.0 266550.0 72000.0 267750.0 ; - RECT 75600.0 275850.0 76800.0 277050.0 ; - RECT 75600.0 265950.0 76800.0 267150.0 ; - RECT 73200.0 269100.0 72000.0 270300.0 ; - RECT 70200.0 271800.0 69000.0 273000.0 ; - RECT 70800.0 275250.0 72000.0 276450.0 ; - RECT 73200.0 266550.0 74400.0 267750.0 ; - RECT 74400.0 271800.0 73200.0 273000.0 ; - RECT 69000.0 271800.0 70200.0 273000.0 ; - RECT 72000.0 269100.0 73200.0 270300.0 ; - RECT 73200.0 271800.0 74400.0 273000.0 ; - RECT 66600.0 277950.0 81000.0 278850.0 ; - RECT 66600.0 264150.0 81000.0 265050.0 ; - RECT 87600.0 276450.0 88800.0 278400.0 ; - RECT 87600.0 264600.0 88800.0 266550.0 ; - RECT 82800.0 265950.0 84000.0 264150.0 ; - RECT 82800.0 275250.0 84000.0 278850.0 ; - RECT 85500.0 265950.0 86400.0 275250.0 ; - RECT 82800.0 275250.0 84000.0 276450.0 ; - RECT 85200.0 275250.0 86400.0 276450.0 ; - RECT 85200.0 275250.0 86400.0 276450.0 ; - RECT 82800.0 275250.0 84000.0 276450.0 ; - RECT 82800.0 265950.0 84000.0 267150.0 ; - RECT 85200.0 265950.0 86400.0 267150.0 ; - RECT 85200.0 265950.0 86400.0 267150.0 ; - RECT 82800.0 265950.0 84000.0 267150.0 ; - RECT 87600.0 275850.0 88800.0 277050.0 ; - RECT 87600.0 265950.0 88800.0 267150.0 ; - RECT 83400.0 270600.0 84600.0 271800.0 ; - RECT 83400.0 270600.0 84600.0 271800.0 ; - RECT 85950.0 270750.0 86850.0 271650.0 ; - RECT 81000.0 277950.0 90600.0 278850.0 ; - RECT 81000.0 264150.0 90600.0 265050.0 ; - RECT 53250.0 270600.0 54450.0 271800.0 ; - RECT 55200.0 268200.0 56400.0 269400.0 ; - RECT 72000.0 269100.0 70800.0 270300.0 ; - RECT 63600.0 280350.0 64800.0 278400.0 ; - RECT 63600.0 292200.0 64800.0 290250.0 ; - RECT 58800.0 290850.0 60000.0 292650.0 ; - RECT 58800.0 281550.0 60000.0 277950.0 ; - RECT 61500.0 290850.0 62400.0 281550.0 ; - RECT 58800.0 281550.0 60000.0 280350.0 ; - RECT 61200.0 281550.0 62400.0 280350.0 ; - RECT 61200.0 281550.0 62400.0 280350.0 ; - RECT 58800.0 281550.0 60000.0 280350.0 ; - RECT 58800.0 290850.0 60000.0 289650.0 ; - RECT 61200.0 290850.0 62400.0 289650.0 ; - RECT 61200.0 290850.0 62400.0 289650.0 ; - RECT 58800.0 290850.0 60000.0 289650.0 ; - RECT 63600.0 280950.0 64800.0 279750.0 ; - RECT 63600.0 290850.0 64800.0 289650.0 ; - RECT 59400.0 286200.0 60600.0 285000.0 ; - RECT 59400.0 286200.0 60600.0 285000.0 ; - RECT 61950.0 286050.0 62850.0 285150.0 ; - RECT 57000.0 278850.0 66600.0 277950.0 ; - RECT 57000.0 292650.0 66600.0 291750.0 ; - RECT 68400.0 290250.0 69600.0 292650.0 ; - RECT 68400.0 281550.0 69600.0 277950.0 ; - RECT 73200.0 281550.0 74400.0 277950.0 ; - RECT 75600.0 280350.0 76800.0 278400.0 ; - RECT 75600.0 292200.0 76800.0 290250.0 ; - RECT 68400.0 281550.0 69600.0 280350.0 ; - RECT 70800.0 281550.0 72000.0 280350.0 ; - RECT 70800.0 281550.0 72000.0 280350.0 ; - RECT 68400.0 281550.0 69600.0 280350.0 ; - RECT 70800.0 281550.0 72000.0 280350.0 ; - RECT 73200.0 281550.0 74400.0 280350.0 ; - RECT 73200.0 281550.0 74400.0 280350.0 ; - RECT 70800.0 281550.0 72000.0 280350.0 ; - RECT 68400.0 290250.0 69600.0 289050.0 ; - RECT 70800.0 290250.0 72000.0 289050.0 ; - RECT 70800.0 290250.0 72000.0 289050.0 ; - RECT 68400.0 290250.0 69600.0 289050.0 ; - RECT 70800.0 290250.0 72000.0 289050.0 ; - RECT 73200.0 290250.0 74400.0 289050.0 ; - RECT 73200.0 290250.0 74400.0 289050.0 ; - RECT 70800.0 290250.0 72000.0 289050.0 ; - RECT 75600.0 280950.0 76800.0 279750.0 ; - RECT 75600.0 290850.0 76800.0 289650.0 ; - RECT 73200.0 287700.0 72000.0 286500.0 ; - RECT 70200.0 285000.0 69000.0 283800.0 ; - RECT 70800.0 281550.0 72000.0 280350.0 ; - RECT 73200.0 290250.0 74400.0 289050.0 ; - RECT 74400.0 285000.0 73200.0 283800.0 ; - RECT 69000.0 285000.0 70200.0 283800.0 ; - RECT 72000.0 287700.0 73200.0 286500.0 ; - RECT 73200.0 285000.0 74400.0 283800.0 ; - RECT 66600.0 278850.0 81000.0 277950.0 ; - RECT 66600.0 292650.0 81000.0 291750.0 ; - RECT 87600.0 280350.0 88800.0 278400.0 ; - RECT 87600.0 292200.0 88800.0 290250.0 ; - RECT 82800.0 290850.0 84000.0 292650.0 ; - RECT 82800.0 281550.0 84000.0 277950.0 ; - RECT 85500.0 290850.0 86400.0 281550.0 ; - RECT 82800.0 281550.0 84000.0 280350.0 ; - RECT 85200.0 281550.0 86400.0 280350.0 ; - RECT 85200.0 281550.0 86400.0 280350.0 ; - RECT 82800.0 281550.0 84000.0 280350.0 ; - RECT 82800.0 290850.0 84000.0 289650.0 ; - RECT 85200.0 290850.0 86400.0 289650.0 ; - RECT 85200.0 290850.0 86400.0 289650.0 ; - RECT 82800.0 290850.0 84000.0 289650.0 ; - RECT 87600.0 280950.0 88800.0 279750.0 ; - RECT 87600.0 290850.0 88800.0 289650.0 ; - RECT 83400.0 286200.0 84600.0 285000.0 ; - RECT 83400.0 286200.0 84600.0 285000.0 ; - RECT 85950.0 286050.0 86850.0 285150.0 ; - RECT 81000.0 278850.0 90600.0 277950.0 ; - RECT 81000.0 292650.0 90600.0 291750.0 ; - RECT 53250.0 285000.0 54450.0 286200.0 ; - RECT 55200.0 287400.0 56400.0 288600.0 ; - RECT 72000.0 286500.0 70800.0 287700.0 ; - RECT 63600.0 304050.0 64800.0 306000.0 ; - RECT 63600.0 292200.0 64800.0 294150.0 ; - RECT 58800.0 293550.0 60000.0 291750.0 ; - RECT 58800.0 302850.0 60000.0 306450.0 ; - RECT 61500.0 293550.0 62400.0 302850.0 ; - RECT 58800.0 302850.0 60000.0 304050.0 ; - RECT 61200.0 302850.0 62400.0 304050.0 ; - RECT 61200.0 302850.0 62400.0 304050.0 ; - RECT 58800.0 302850.0 60000.0 304050.0 ; - RECT 58800.0 293550.0 60000.0 294750.0 ; - RECT 61200.0 293550.0 62400.0 294750.0 ; - RECT 61200.0 293550.0 62400.0 294750.0 ; - RECT 58800.0 293550.0 60000.0 294750.0 ; - RECT 63600.0 303450.0 64800.0 304650.0 ; - RECT 63600.0 293550.0 64800.0 294750.0 ; - RECT 59400.0 298200.0 60600.0 299400.0 ; - RECT 59400.0 298200.0 60600.0 299400.0 ; - RECT 61950.0 298350.0 62850.0 299250.0 ; - RECT 57000.0 305550.0 66600.0 306450.0 ; - RECT 57000.0 291750.0 66600.0 292650.0 ; - RECT 68400.0 294150.0 69600.0 291750.0 ; - RECT 68400.0 302850.0 69600.0 306450.0 ; - RECT 73200.0 302850.0 74400.0 306450.0 ; - RECT 75600.0 304050.0 76800.0 306000.0 ; - RECT 75600.0 292200.0 76800.0 294150.0 ; - RECT 68400.0 302850.0 69600.0 304050.0 ; - RECT 70800.0 302850.0 72000.0 304050.0 ; - RECT 70800.0 302850.0 72000.0 304050.0 ; - RECT 68400.0 302850.0 69600.0 304050.0 ; - RECT 70800.0 302850.0 72000.0 304050.0 ; - RECT 73200.0 302850.0 74400.0 304050.0 ; - RECT 73200.0 302850.0 74400.0 304050.0 ; - RECT 70800.0 302850.0 72000.0 304050.0 ; - RECT 68400.0 294150.0 69600.0 295350.0 ; - RECT 70800.0 294150.0 72000.0 295350.0 ; - RECT 70800.0 294150.0 72000.0 295350.0 ; - RECT 68400.0 294150.0 69600.0 295350.0 ; - RECT 70800.0 294150.0 72000.0 295350.0 ; - RECT 73200.0 294150.0 74400.0 295350.0 ; - RECT 73200.0 294150.0 74400.0 295350.0 ; - RECT 70800.0 294150.0 72000.0 295350.0 ; - RECT 75600.0 303450.0 76800.0 304650.0 ; - RECT 75600.0 293550.0 76800.0 294750.0 ; - RECT 73200.0 296700.0 72000.0 297900.0 ; - RECT 70200.0 299400.0 69000.0 300600.0 ; - RECT 70800.0 302850.0 72000.0 304050.0 ; - RECT 73200.0 294150.0 74400.0 295350.0 ; - RECT 74400.0 299400.0 73200.0 300600.0 ; - RECT 69000.0 299400.0 70200.0 300600.0 ; - RECT 72000.0 296700.0 73200.0 297900.0 ; - RECT 73200.0 299400.0 74400.0 300600.0 ; - RECT 66600.0 305550.0 81000.0 306450.0 ; - RECT 66600.0 291750.0 81000.0 292650.0 ; - RECT 87600.0 304050.0 88800.0 306000.0 ; - RECT 87600.0 292200.0 88800.0 294150.0 ; - RECT 82800.0 293550.0 84000.0 291750.0 ; - RECT 82800.0 302850.0 84000.0 306450.0 ; - RECT 85500.0 293550.0 86400.0 302850.0 ; - RECT 82800.0 302850.0 84000.0 304050.0 ; - RECT 85200.0 302850.0 86400.0 304050.0 ; - RECT 85200.0 302850.0 86400.0 304050.0 ; - RECT 82800.0 302850.0 84000.0 304050.0 ; - RECT 82800.0 293550.0 84000.0 294750.0 ; - RECT 85200.0 293550.0 86400.0 294750.0 ; - RECT 85200.0 293550.0 86400.0 294750.0 ; - RECT 82800.0 293550.0 84000.0 294750.0 ; - RECT 87600.0 303450.0 88800.0 304650.0 ; - RECT 87600.0 293550.0 88800.0 294750.0 ; - RECT 83400.0 298200.0 84600.0 299400.0 ; - RECT 83400.0 298200.0 84600.0 299400.0 ; - RECT 85950.0 298350.0 86850.0 299250.0 ; - RECT 81000.0 305550.0 90600.0 306450.0 ; - RECT 81000.0 291750.0 90600.0 292650.0 ; - RECT 53250.0 298200.0 54450.0 299400.0 ; - RECT 55200.0 295800.0 56400.0 297000.0 ; - RECT 72000.0 296700.0 70800.0 297900.0 ; - RECT 63600.0 307950.0 64800.0 306000.0 ; - RECT 63600.0 319800.0 64800.0 317850.0 ; - RECT 58800.0 318450.0 60000.0 320250.0 ; - RECT 58800.0 309150.0 60000.0 305550.0 ; - RECT 61500.0 318450.0 62400.0 309150.0 ; - RECT 58800.0 309150.0 60000.0 307950.0 ; - RECT 61200.0 309150.0 62400.0 307950.0 ; - RECT 61200.0 309150.0 62400.0 307950.0 ; - RECT 58800.0 309150.0 60000.0 307950.0 ; - RECT 58800.0 318450.0 60000.0 317250.0 ; - RECT 61200.0 318450.0 62400.0 317250.0 ; - RECT 61200.0 318450.0 62400.0 317250.0 ; - RECT 58800.0 318450.0 60000.0 317250.0 ; - RECT 63600.0 308550.0 64800.0 307350.0 ; - RECT 63600.0 318450.0 64800.0 317250.0 ; - RECT 59400.0 313800.0 60600.0 312600.0 ; - RECT 59400.0 313800.0 60600.0 312600.0 ; - RECT 61950.0 313650.0 62850.0 312750.0 ; - RECT 57000.0 306450.0 66600.0 305550.0 ; - RECT 57000.0 320250.0 66600.0 319350.0 ; - RECT 68400.0 317850.0 69600.0 320250.0 ; - RECT 68400.0 309150.0 69600.0 305550.0 ; - RECT 73200.0 309150.0 74400.0 305550.0 ; - RECT 75600.0 307950.0 76800.0 306000.0 ; - RECT 75600.0 319800.0 76800.0 317850.0 ; - RECT 68400.0 309150.0 69600.0 307950.0 ; - RECT 70800.0 309150.0 72000.0 307950.0 ; - RECT 70800.0 309150.0 72000.0 307950.0 ; - RECT 68400.0 309150.0 69600.0 307950.0 ; - RECT 70800.0 309150.0 72000.0 307950.0 ; - RECT 73200.0 309150.0 74400.0 307950.0 ; - RECT 73200.0 309150.0 74400.0 307950.0 ; - RECT 70800.0 309150.0 72000.0 307950.0 ; - RECT 68400.0 317850.0 69600.0 316650.0 ; - RECT 70800.0 317850.0 72000.0 316650.0 ; - RECT 70800.0 317850.0 72000.0 316650.0 ; - RECT 68400.0 317850.0 69600.0 316650.0 ; - RECT 70800.0 317850.0 72000.0 316650.0 ; - RECT 73200.0 317850.0 74400.0 316650.0 ; - RECT 73200.0 317850.0 74400.0 316650.0 ; - RECT 70800.0 317850.0 72000.0 316650.0 ; - RECT 75600.0 308550.0 76800.0 307350.0 ; - RECT 75600.0 318450.0 76800.0 317250.0 ; - RECT 73200.0 315300.0 72000.0 314100.0 ; - RECT 70200.0 312600.0 69000.0 311400.0 ; - RECT 70800.0 309150.0 72000.0 307950.0 ; - RECT 73200.0 317850.0 74400.0 316650.0 ; - RECT 74400.0 312600.0 73200.0 311400.0 ; - RECT 69000.0 312600.0 70200.0 311400.0 ; - RECT 72000.0 315300.0 73200.0 314100.0 ; - RECT 73200.0 312600.0 74400.0 311400.0 ; - RECT 66600.0 306450.0 81000.0 305550.0 ; - RECT 66600.0 320250.0 81000.0 319350.0 ; - RECT 87600.0 307950.0 88800.0 306000.0 ; - RECT 87600.0 319800.0 88800.0 317850.0 ; - RECT 82800.0 318450.0 84000.0 320250.0 ; - RECT 82800.0 309150.0 84000.0 305550.0 ; - RECT 85500.0 318450.0 86400.0 309150.0 ; - RECT 82800.0 309150.0 84000.0 307950.0 ; - RECT 85200.0 309150.0 86400.0 307950.0 ; - RECT 85200.0 309150.0 86400.0 307950.0 ; - RECT 82800.0 309150.0 84000.0 307950.0 ; - RECT 82800.0 318450.0 84000.0 317250.0 ; - RECT 85200.0 318450.0 86400.0 317250.0 ; - RECT 85200.0 318450.0 86400.0 317250.0 ; - RECT 82800.0 318450.0 84000.0 317250.0 ; - RECT 87600.0 308550.0 88800.0 307350.0 ; - RECT 87600.0 318450.0 88800.0 317250.0 ; - RECT 83400.0 313800.0 84600.0 312600.0 ; - RECT 83400.0 313800.0 84600.0 312600.0 ; - RECT 85950.0 313650.0 86850.0 312750.0 ; - RECT 81000.0 306450.0 90600.0 305550.0 ; - RECT 81000.0 320250.0 90600.0 319350.0 ; - RECT 53250.0 312600.0 54450.0 313800.0 ; - RECT 55200.0 315000.0 56400.0 316200.0 ; - RECT 72000.0 314100.0 70800.0 315300.0 ; - RECT 63600.0 331650.0 64800.0 333600.0 ; - RECT 63600.0 319800.0 64800.0 321750.0 ; - RECT 58800.0 321150.0 60000.0 319350.0 ; - RECT 58800.0 330450.0 60000.0 334050.0 ; - RECT 61500.0 321150.0 62400.0 330450.0 ; - RECT 58800.0 330450.0 60000.0 331650.0 ; - RECT 61200.0 330450.0 62400.0 331650.0 ; - RECT 61200.0 330450.0 62400.0 331650.0 ; - RECT 58800.0 330450.0 60000.0 331650.0 ; - RECT 58800.0 321150.0 60000.0 322350.0 ; - RECT 61200.0 321150.0 62400.0 322350.0 ; - RECT 61200.0 321150.0 62400.0 322350.0 ; - RECT 58800.0 321150.0 60000.0 322350.0 ; - RECT 63600.0 331050.0 64800.0 332250.0 ; - RECT 63600.0 321150.0 64800.0 322350.0 ; - RECT 59400.0 325800.0 60600.0 327000.0 ; - RECT 59400.0 325800.0 60600.0 327000.0 ; - RECT 61950.0 325950.0 62850.0 326850.0 ; - RECT 57000.0 333150.0 66600.0 334050.0 ; - RECT 57000.0 319350.0 66600.0 320250.0 ; - RECT 68400.0 321750.0 69600.0 319350.0 ; - RECT 68400.0 330450.0 69600.0 334050.0 ; - RECT 73200.0 330450.0 74400.0 334050.0 ; - RECT 75600.0 331650.0 76800.0 333600.0 ; - RECT 75600.0 319800.0 76800.0 321750.0 ; - RECT 68400.0 330450.0 69600.0 331650.0 ; - RECT 70800.0 330450.0 72000.0 331650.0 ; - RECT 70800.0 330450.0 72000.0 331650.0 ; - RECT 68400.0 330450.0 69600.0 331650.0 ; - RECT 70800.0 330450.0 72000.0 331650.0 ; - RECT 73200.0 330450.0 74400.0 331650.0 ; - RECT 73200.0 330450.0 74400.0 331650.0 ; - RECT 70800.0 330450.0 72000.0 331650.0 ; - RECT 68400.0 321750.0 69600.0 322950.0 ; - RECT 70800.0 321750.0 72000.0 322950.0 ; - RECT 70800.0 321750.0 72000.0 322950.0 ; - RECT 68400.0 321750.0 69600.0 322950.0 ; - RECT 70800.0 321750.0 72000.0 322950.0 ; - RECT 73200.0 321750.0 74400.0 322950.0 ; - RECT 73200.0 321750.0 74400.0 322950.0 ; - RECT 70800.0 321750.0 72000.0 322950.0 ; - RECT 75600.0 331050.0 76800.0 332250.0 ; - RECT 75600.0 321150.0 76800.0 322350.0 ; - RECT 73200.0 324300.0 72000.0 325500.0 ; - RECT 70200.0 327000.0 69000.0 328200.0 ; - RECT 70800.0 330450.0 72000.0 331650.0 ; - RECT 73200.0 321750.0 74400.0 322950.0 ; - RECT 74400.0 327000.0 73200.0 328200.0 ; - RECT 69000.0 327000.0 70200.0 328200.0 ; - RECT 72000.0 324300.0 73200.0 325500.0 ; - RECT 73200.0 327000.0 74400.0 328200.0 ; - RECT 66600.0 333150.0 81000.0 334050.0 ; - RECT 66600.0 319350.0 81000.0 320250.0 ; - RECT 87600.0 331650.0 88800.0 333600.0 ; - RECT 87600.0 319800.0 88800.0 321750.0 ; - RECT 82800.0 321150.0 84000.0 319350.0 ; - RECT 82800.0 330450.0 84000.0 334050.0 ; - RECT 85500.0 321150.0 86400.0 330450.0 ; - RECT 82800.0 330450.0 84000.0 331650.0 ; - RECT 85200.0 330450.0 86400.0 331650.0 ; - RECT 85200.0 330450.0 86400.0 331650.0 ; - RECT 82800.0 330450.0 84000.0 331650.0 ; - RECT 82800.0 321150.0 84000.0 322350.0 ; - RECT 85200.0 321150.0 86400.0 322350.0 ; - RECT 85200.0 321150.0 86400.0 322350.0 ; - RECT 82800.0 321150.0 84000.0 322350.0 ; - RECT 87600.0 331050.0 88800.0 332250.0 ; - RECT 87600.0 321150.0 88800.0 322350.0 ; - RECT 83400.0 325800.0 84600.0 327000.0 ; - RECT 83400.0 325800.0 84600.0 327000.0 ; - RECT 85950.0 325950.0 86850.0 326850.0 ; - RECT 81000.0 333150.0 90600.0 334050.0 ; - RECT 81000.0 319350.0 90600.0 320250.0 ; - RECT 53250.0 325800.0 54450.0 327000.0 ; - RECT 55200.0 323400.0 56400.0 324600.0 ; - RECT 72000.0 324300.0 70800.0 325500.0 ; - RECT 63600.0 335550.0 64800.0 333600.0 ; - RECT 63600.0 347400.0 64800.0 345450.0 ; - RECT 58800.0 346050.0 60000.0 347850.0 ; - RECT 58800.0 336750.0 60000.0 333150.0 ; - RECT 61500.0 346050.0 62400.0 336750.0 ; - RECT 58800.0 336750.0 60000.0 335550.0 ; - RECT 61200.0 336750.0 62400.0 335550.0 ; - RECT 61200.0 336750.0 62400.0 335550.0 ; - RECT 58800.0 336750.0 60000.0 335550.0 ; - RECT 58800.0 346050.0 60000.0 344850.0 ; - RECT 61200.0 346050.0 62400.0 344850.0 ; - RECT 61200.0 346050.0 62400.0 344850.0 ; - RECT 58800.0 346050.0 60000.0 344850.0 ; - RECT 63600.0 336150.0 64800.0 334950.0 ; - RECT 63600.0 346050.0 64800.0 344850.0 ; - RECT 59400.0 341400.0 60600.0 340200.0 ; - RECT 59400.0 341400.0 60600.0 340200.0 ; - RECT 61950.0 341250.0 62850.0 340350.0 ; - RECT 57000.0 334050.0 66600.0 333150.0 ; - RECT 57000.0 347850.0 66600.0 346950.0 ; - RECT 68400.0 345450.0 69600.0 347850.0 ; - RECT 68400.0 336750.0 69600.0 333150.0 ; - RECT 73200.0 336750.0 74400.0 333150.0 ; - RECT 75600.0 335550.0 76800.0 333600.0 ; - RECT 75600.0 347400.0 76800.0 345450.0 ; - RECT 68400.0 336750.0 69600.0 335550.0 ; - RECT 70800.0 336750.0 72000.0 335550.0 ; - RECT 70800.0 336750.0 72000.0 335550.0 ; - RECT 68400.0 336750.0 69600.0 335550.0 ; - RECT 70800.0 336750.0 72000.0 335550.0 ; - RECT 73200.0 336750.0 74400.0 335550.0 ; - RECT 73200.0 336750.0 74400.0 335550.0 ; - RECT 70800.0 336750.0 72000.0 335550.0 ; - RECT 68400.0 345450.0 69600.0 344250.0 ; - RECT 70800.0 345450.0 72000.0 344250.0 ; - RECT 70800.0 345450.0 72000.0 344250.0 ; - RECT 68400.0 345450.0 69600.0 344250.0 ; - RECT 70800.0 345450.0 72000.0 344250.0 ; - RECT 73200.0 345450.0 74400.0 344250.0 ; - RECT 73200.0 345450.0 74400.0 344250.0 ; - RECT 70800.0 345450.0 72000.0 344250.0 ; - RECT 75600.0 336150.0 76800.0 334950.0 ; - RECT 75600.0 346050.0 76800.0 344850.0 ; - RECT 73200.0 342900.0 72000.0 341700.0 ; - RECT 70200.0 340200.0 69000.0 339000.0 ; - RECT 70800.0 336750.0 72000.0 335550.0 ; - RECT 73200.0 345450.0 74400.0 344250.0 ; - RECT 74400.0 340200.0 73200.0 339000.0 ; - RECT 69000.0 340200.0 70200.0 339000.0 ; - RECT 72000.0 342900.0 73200.0 341700.0 ; - RECT 73200.0 340200.0 74400.0 339000.0 ; - RECT 66600.0 334050.0 81000.0 333150.0 ; - RECT 66600.0 347850.0 81000.0 346950.0 ; - RECT 87600.0 335550.0 88800.0 333600.0 ; - RECT 87600.0 347400.0 88800.0 345450.0 ; - RECT 82800.0 346050.0 84000.0 347850.0 ; - RECT 82800.0 336750.0 84000.0 333150.0 ; - RECT 85500.0 346050.0 86400.0 336750.0 ; - RECT 82800.0 336750.0 84000.0 335550.0 ; - RECT 85200.0 336750.0 86400.0 335550.0 ; - RECT 85200.0 336750.0 86400.0 335550.0 ; - RECT 82800.0 336750.0 84000.0 335550.0 ; - RECT 82800.0 346050.0 84000.0 344850.0 ; - RECT 85200.0 346050.0 86400.0 344850.0 ; - RECT 85200.0 346050.0 86400.0 344850.0 ; - RECT 82800.0 346050.0 84000.0 344850.0 ; - RECT 87600.0 336150.0 88800.0 334950.0 ; - RECT 87600.0 346050.0 88800.0 344850.0 ; - RECT 83400.0 341400.0 84600.0 340200.0 ; - RECT 83400.0 341400.0 84600.0 340200.0 ; - RECT 85950.0 341250.0 86850.0 340350.0 ; - RECT 81000.0 334050.0 90600.0 333150.0 ; - RECT 81000.0 347850.0 90600.0 346950.0 ; - RECT 53250.0 340200.0 54450.0 341400.0 ; - RECT 55200.0 342600.0 56400.0 343800.0 ; - RECT 72000.0 341700.0 70800.0 342900.0 ; - RECT 63600.0 359250.0 64800.0 361200.0 ; - RECT 63600.0 347400.0 64800.0 349350.0 ; - RECT 58800.0 348750.0 60000.0 346950.0 ; - RECT 58800.0 358050.0 60000.0 361650.0 ; - RECT 61500.0 348750.0 62400.0 358050.0 ; - RECT 58800.0 358050.0 60000.0 359250.0 ; - RECT 61200.0 358050.0 62400.0 359250.0 ; - RECT 61200.0 358050.0 62400.0 359250.0 ; - RECT 58800.0 358050.0 60000.0 359250.0 ; - RECT 58800.0 348750.0 60000.0 349950.0 ; - RECT 61200.0 348750.0 62400.0 349950.0 ; - RECT 61200.0 348750.0 62400.0 349950.0 ; - RECT 58800.0 348750.0 60000.0 349950.0 ; - RECT 63600.0 358650.0 64800.0 359850.0 ; - RECT 63600.0 348750.0 64800.0 349950.0 ; - RECT 59400.0 353400.0 60600.0 354600.0 ; - RECT 59400.0 353400.0 60600.0 354600.0 ; - RECT 61950.0 353550.0 62850.0 354450.0 ; - RECT 57000.0 360750.0 66600.0 361650.0 ; - RECT 57000.0 346950.0 66600.0 347850.0 ; - RECT 68400.0 349350.0 69600.0 346950.0 ; - RECT 68400.0 358050.0 69600.0 361650.0 ; - RECT 73200.0 358050.0 74400.0 361650.0 ; - RECT 75600.0 359250.0 76800.0 361200.0 ; - RECT 75600.0 347400.0 76800.0 349350.0 ; - RECT 68400.0 358050.0 69600.0 359250.0 ; - RECT 70800.0 358050.0 72000.0 359250.0 ; - RECT 70800.0 358050.0 72000.0 359250.0 ; - RECT 68400.0 358050.0 69600.0 359250.0 ; - RECT 70800.0 358050.0 72000.0 359250.0 ; - RECT 73200.0 358050.0 74400.0 359250.0 ; - RECT 73200.0 358050.0 74400.0 359250.0 ; - RECT 70800.0 358050.0 72000.0 359250.0 ; - RECT 68400.0 349350.0 69600.0 350550.0 ; - RECT 70800.0 349350.0 72000.0 350550.0 ; - RECT 70800.0 349350.0 72000.0 350550.0 ; - RECT 68400.0 349350.0 69600.0 350550.0 ; - RECT 70800.0 349350.0 72000.0 350550.0 ; - RECT 73200.0 349350.0 74400.0 350550.0 ; - RECT 73200.0 349350.0 74400.0 350550.0 ; - RECT 70800.0 349350.0 72000.0 350550.0 ; - RECT 75600.0 358650.0 76800.0 359850.0 ; - RECT 75600.0 348750.0 76800.0 349950.0 ; - RECT 73200.0 351900.0 72000.0 353100.0 ; - RECT 70200.0 354600.0 69000.0 355800.0 ; - RECT 70800.0 358050.0 72000.0 359250.0 ; - RECT 73200.0 349350.0 74400.0 350550.0 ; - RECT 74400.0 354600.0 73200.0 355800.0 ; - RECT 69000.0 354600.0 70200.0 355800.0 ; - RECT 72000.0 351900.0 73200.0 353100.0 ; - RECT 73200.0 354600.0 74400.0 355800.0 ; - RECT 66600.0 360750.0 81000.0 361650.0 ; - RECT 66600.0 346950.0 81000.0 347850.0 ; - RECT 87600.0 359250.0 88800.0 361200.0 ; - RECT 87600.0 347400.0 88800.0 349350.0 ; - RECT 82800.0 348750.0 84000.0 346950.0 ; - RECT 82800.0 358050.0 84000.0 361650.0 ; - RECT 85500.0 348750.0 86400.0 358050.0 ; - RECT 82800.0 358050.0 84000.0 359250.0 ; - RECT 85200.0 358050.0 86400.0 359250.0 ; - RECT 85200.0 358050.0 86400.0 359250.0 ; - RECT 82800.0 358050.0 84000.0 359250.0 ; - RECT 82800.0 348750.0 84000.0 349950.0 ; - RECT 85200.0 348750.0 86400.0 349950.0 ; - RECT 85200.0 348750.0 86400.0 349950.0 ; - RECT 82800.0 348750.0 84000.0 349950.0 ; - RECT 87600.0 358650.0 88800.0 359850.0 ; - RECT 87600.0 348750.0 88800.0 349950.0 ; - RECT 83400.0 353400.0 84600.0 354600.0 ; - RECT 83400.0 353400.0 84600.0 354600.0 ; - RECT 85950.0 353550.0 86850.0 354450.0 ; - RECT 81000.0 360750.0 90600.0 361650.0 ; - RECT 81000.0 346950.0 90600.0 347850.0 ; - RECT 53250.0 353400.0 54450.0 354600.0 ; - RECT 55200.0 351000.0 56400.0 352200.0 ; - RECT 72000.0 351900.0 70800.0 353100.0 ; - RECT 63600.0 363150.0 64800.0 361200.0 ; - RECT 63600.0 375000.0 64800.0 373050.0 ; - RECT 58800.0 373650.0 60000.0 375450.0 ; - RECT 58800.0 364350.0 60000.0 360750.0 ; - RECT 61500.0 373650.0 62400.0 364350.0 ; - RECT 58800.0 364350.0 60000.0 363150.0 ; - RECT 61200.0 364350.0 62400.0 363150.0 ; - RECT 61200.0 364350.0 62400.0 363150.0 ; - RECT 58800.0 364350.0 60000.0 363150.0 ; - RECT 58800.0 373650.0 60000.0 372450.0 ; - RECT 61200.0 373650.0 62400.0 372450.0 ; - RECT 61200.0 373650.0 62400.0 372450.0 ; - RECT 58800.0 373650.0 60000.0 372450.0 ; - RECT 63600.0 363750.0 64800.0 362550.0 ; - RECT 63600.0 373650.0 64800.0 372450.0 ; - RECT 59400.0 369000.0 60600.0 367800.0 ; - RECT 59400.0 369000.0 60600.0 367800.0 ; - RECT 61950.0 368850.0 62850.0 367950.0 ; - RECT 57000.0 361650.0 66600.0 360750.0 ; - RECT 57000.0 375450.0 66600.0 374550.0 ; - RECT 68400.0 373050.0 69600.0 375450.0 ; - RECT 68400.0 364350.0 69600.0 360750.0 ; - RECT 73200.0 364350.0 74400.0 360750.0 ; - RECT 75600.0 363150.0 76800.0 361200.0 ; - RECT 75600.0 375000.0 76800.0 373050.0 ; - RECT 68400.0 364350.0 69600.0 363150.0 ; - RECT 70800.0 364350.0 72000.0 363150.0 ; - RECT 70800.0 364350.0 72000.0 363150.0 ; - RECT 68400.0 364350.0 69600.0 363150.0 ; - RECT 70800.0 364350.0 72000.0 363150.0 ; - RECT 73200.0 364350.0 74400.0 363150.0 ; - RECT 73200.0 364350.0 74400.0 363150.0 ; - RECT 70800.0 364350.0 72000.0 363150.0 ; - RECT 68400.0 373050.0 69600.0 371850.0 ; - RECT 70800.0 373050.0 72000.0 371850.0 ; - RECT 70800.0 373050.0 72000.0 371850.0 ; - RECT 68400.0 373050.0 69600.0 371850.0 ; - RECT 70800.0 373050.0 72000.0 371850.0 ; - RECT 73200.0 373050.0 74400.0 371850.0 ; - RECT 73200.0 373050.0 74400.0 371850.0 ; - RECT 70800.0 373050.0 72000.0 371850.0 ; - RECT 75600.0 363750.0 76800.0 362550.0 ; - RECT 75600.0 373650.0 76800.0 372450.0 ; - RECT 73200.0 370500.0 72000.0 369300.0 ; - RECT 70200.0 367800.0 69000.0 366600.0 ; - RECT 70800.0 364350.0 72000.0 363150.0 ; - RECT 73200.0 373050.0 74400.0 371850.0 ; - RECT 74400.0 367800.0 73200.0 366600.0 ; - RECT 69000.0 367800.0 70200.0 366600.0 ; - RECT 72000.0 370500.0 73200.0 369300.0 ; - RECT 73200.0 367800.0 74400.0 366600.0 ; - RECT 66600.0 361650.0 81000.0 360750.0 ; - RECT 66600.0 375450.0 81000.0 374550.0 ; - RECT 87600.0 363150.0 88800.0 361200.0 ; - RECT 87600.0 375000.0 88800.0 373050.0 ; - RECT 82800.0 373650.0 84000.0 375450.0 ; - RECT 82800.0 364350.0 84000.0 360750.0 ; - RECT 85500.0 373650.0 86400.0 364350.0 ; - RECT 82800.0 364350.0 84000.0 363150.0 ; - RECT 85200.0 364350.0 86400.0 363150.0 ; - RECT 85200.0 364350.0 86400.0 363150.0 ; - RECT 82800.0 364350.0 84000.0 363150.0 ; - RECT 82800.0 373650.0 84000.0 372450.0 ; - RECT 85200.0 373650.0 86400.0 372450.0 ; - RECT 85200.0 373650.0 86400.0 372450.0 ; - RECT 82800.0 373650.0 84000.0 372450.0 ; - RECT 87600.0 363750.0 88800.0 362550.0 ; - RECT 87600.0 373650.0 88800.0 372450.0 ; - RECT 83400.0 369000.0 84600.0 367800.0 ; - RECT 83400.0 369000.0 84600.0 367800.0 ; - RECT 85950.0 368850.0 86850.0 367950.0 ; - RECT 81000.0 361650.0 90600.0 360750.0 ; - RECT 81000.0 375450.0 90600.0 374550.0 ; - RECT 53250.0 367800.0 54450.0 369000.0 ; - RECT 55200.0 370200.0 56400.0 371400.0 ; - RECT 72000.0 369300.0 70800.0 370500.0 ; - RECT 63600.0 386850.0 64800.0 388800.0 ; - RECT 63600.0 375000.0 64800.0 376950.0 ; - RECT 58800.0 376350.0 60000.0 374550.0 ; - RECT 58800.0 385650.0 60000.0 389250.0 ; - RECT 61500.0 376350.0 62400.0 385650.0 ; - RECT 58800.0 385650.0 60000.0 386850.0 ; - RECT 61200.0 385650.0 62400.0 386850.0 ; - RECT 61200.0 385650.0 62400.0 386850.0 ; - RECT 58800.0 385650.0 60000.0 386850.0 ; - RECT 58800.0 376350.0 60000.0 377550.0 ; - RECT 61200.0 376350.0 62400.0 377550.0 ; - RECT 61200.0 376350.0 62400.0 377550.0 ; - RECT 58800.0 376350.0 60000.0 377550.0 ; - RECT 63600.0 386250.0 64800.0 387450.0 ; - RECT 63600.0 376350.0 64800.0 377550.0 ; - RECT 59400.0 381000.0 60600.0 382200.0 ; - RECT 59400.0 381000.0 60600.0 382200.0 ; - RECT 61950.0 381150.0 62850.0 382050.0 ; - RECT 57000.0 388350.0 66600.0 389250.0 ; - RECT 57000.0 374550.0 66600.0 375450.0 ; - RECT 68400.0 376950.0 69600.0 374550.0 ; - RECT 68400.0 385650.0 69600.0 389250.0 ; - RECT 73200.0 385650.0 74400.0 389250.0 ; - RECT 75600.0 386850.0 76800.0 388800.0 ; - RECT 75600.0 375000.0 76800.0 376950.0 ; - RECT 68400.0 385650.0 69600.0 386850.0 ; - RECT 70800.0 385650.0 72000.0 386850.0 ; - RECT 70800.0 385650.0 72000.0 386850.0 ; - RECT 68400.0 385650.0 69600.0 386850.0 ; - RECT 70800.0 385650.0 72000.0 386850.0 ; - RECT 73200.0 385650.0 74400.0 386850.0 ; - RECT 73200.0 385650.0 74400.0 386850.0 ; - RECT 70800.0 385650.0 72000.0 386850.0 ; - RECT 68400.0 376950.0 69600.0 378150.0 ; - RECT 70800.0 376950.0 72000.0 378150.0 ; - RECT 70800.0 376950.0 72000.0 378150.0 ; - RECT 68400.0 376950.0 69600.0 378150.0 ; - RECT 70800.0 376950.0 72000.0 378150.0 ; - RECT 73200.0 376950.0 74400.0 378150.0 ; - RECT 73200.0 376950.0 74400.0 378150.0 ; - RECT 70800.0 376950.0 72000.0 378150.0 ; - RECT 75600.0 386250.0 76800.0 387450.0 ; - RECT 75600.0 376350.0 76800.0 377550.0 ; - RECT 73200.0 379500.0 72000.0 380700.0 ; - RECT 70200.0 382200.0 69000.0 383400.0 ; - RECT 70800.0 385650.0 72000.0 386850.0 ; - RECT 73200.0 376950.0 74400.0 378150.0 ; - RECT 74400.0 382200.0 73200.0 383400.0 ; - RECT 69000.0 382200.0 70200.0 383400.0 ; - RECT 72000.0 379500.0 73200.0 380700.0 ; - RECT 73200.0 382200.0 74400.0 383400.0 ; - RECT 66600.0 388350.0 81000.0 389250.0 ; - RECT 66600.0 374550.0 81000.0 375450.0 ; - RECT 87600.0 386850.0 88800.0 388800.0 ; - RECT 87600.0 375000.0 88800.0 376950.0 ; - RECT 82800.0 376350.0 84000.0 374550.0 ; - RECT 82800.0 385650.0 84000.0 389250.0 ; - RECT 85500.0 376350.0 86400.0 385650.0 ; - RECT 82800.0 385650.0 84000.0 386850.0 ; - RECT 85200.0 385650.0 86400.0 386850.0 ; - RECT 85200.0 385650.0 86400.0 386850.0 ; - RECT 82800.0 385650.0 84000.0 386850.0 ; - RECT 82800.0 376350.0 84000.0 377550.0 ; - RECT 85200.0 376350.0 86400.0 377550.0 ; - RECT 85200.0 376350.0 86400.0 377550.0 ; - RECT 82800.0 376350.0 84000.0 377550.0 ; - RECT 87600.0 386250.0 88800.0 387450.0 ; - RECT 87600.0 376350.0 88800.0 377550.0 ; - RECT 83400.0 381000.0 84600.0 382200.0 ; - RECT 83400.0 381000.0 84600.0 382200.0 ; - RECT 85950.0 381150.0 86850.0 382050.0 ; - RECT 81000.0 388350.0 90600.0 389250.0 ; - RECT 81000.0 374550.0 90600.0 375450.0 ; - RECT 53250.0 381000.0 54450.0 382200.0 ; - RECT 55200.0 378600.0 56400.0 379800.0 ; - RECT 72000.0 379500.0 70800.0 380700.0 ; - RECT 63600.0 390750.0 64800.0 388800.0 ; - RECT 63600.0 402600.0 64800.0 400650.0 ; - RECT 58800.0 401250.0 60000.0 403050.0 ; - RECT 58800.0 391950.0 60000.0 388350.0 ; - RECT 61500.0 401250.0 62400.0 391950.0 ; - RECT 58800.0 391950.0 60000.0 390750.0 ; - RECT 61200.0 391950.0 62400.0 390750.0 ; - RECT 61200.0 391950.0 62400.0 390750.0 ; - RECT 58800.0 391950.0 60000.0 390750.0 ; - RECT 58800.0 401250.0 60000.0 400050.0 ; - RECT 61200.0 401250.0 62400.0 400050.0 ; - RECT 61200.0 401250.0 62400.0 400050.0 ; - RECT 58800.0 401250.0 60000.0 400050.0 ; - RECT 63600.0 391350.0 64800.0 390150.0 ; - RECT 63600.0 401250.0 64800.0 400050.0 ; - RECT 59400.0 396600.0 60600.0 395400.0 ; - RECT 59400.0 396600.0 60600.0 395400.0 ; - RECT 61950.0 396450.0 62850.0 395550.0 ; - RECT 57000.0 389250.0 66600.0 388350.0 ; - RECT 57000.0 403050.0 66600.0 402150.0 ; - RECT 68400.0 400650.0 69600.0 403050.0 ; - RECT 68400.0 391950.0 69600.0 388350.0 ; - RECT 73200.0 391950.0 74400.0 388350.0 ; - RECT 75600.0 390750.0 76800.0 388800.0 ; - RECT 75600.0 402600.0 76800.0 400650.0 ; - RECT 68400.0 391950.0 69600.0 390750.0 ; - RECT 70800.0 391950.0 72000.0 390750.0 ; - RECT 70800.0 391950.0 72000.0 390750.0 ; - RECT 68400.0 391950.0 69600.0 390750.0 ; - RECT 70800.0 391950.0 72000.0 390750.0 ; - RECT 73200.0 391950.0 74400.0 390750.0 ; - RECT 73200.0 391950.0 74400.0 390750.0 ; - RECT 70800.0 391950.0 72000.0 390750.0 ; - RECT 68400.0 400650.0 69600.0 399450.0 ; - RECT 70800.0 400650.0 72000.0 399450.0 ; - RECT 70800.0 400650.0 72000.0 399450.0 ; - RECT 68400.0 400650.0 69600.0 399450.0 ; - RECT 70800.0 400650.0 72000.0 399450.0 ; - RECT 73200.0 400650.0 74400.0 399450.0 ; - RECT 73200.0 400650.0 74400.0 399450.0 ; - RECT 70800.0 400650.0 72000.0 399450.0 ; - RECT 75600.0 391350.0 76800.0 390150.0 ; - RECT 75600.0 401250.0 76800.0 400050.0 ; - RECT 73200.0 398100.0 72000.0 396900.0 ; - RECT 70200.0 395400.0 69000.0 394200.0 ; - RECT 70800.0 391950.0 72000.0 390750.0 ; - RECT 73200.0 400650.0 74400.0 399450.0 ; - RECT 74400.0 395400.0 73200.0 394200.0 ; - RECT 69000.0 395400.0 70200.0 394200.0 ; - RECT 72000.0 398100.0 73200.0 396900.0 ; - RECT 73200.0 395400.0 74400.0 394200.0 ; - RECT 66600.0 389250.0 81000.0 388350.0 ; - RECT 66600.0 403050.0 81000.0 402150.0 ; - RECT 87600.0 390750.0 88800.0 388800.0 ; - RECT 87600.0 402600.0 88800.0 400650.0 ; - RECT 82800.0 401250.0 84000.0 403050.0 ; - RECT 82800.0 391950.0 84000.0 388350.0 ; - RECT 85500.0 401250.0 86400.0 391950.0 ; - RECT 82800.0 391950.0 84000.0 390750.0 ; - RECT 85200.0 391950.0 86400.0 390750.0 ; - RECT 85200.0 391950.0 86400.0 390750.0 ; - RECT 82800.0 391950.0 84000.0 390750.0 ; - RECT 82800.0 401250.0 84000.0 400050.0 ; - RECT 85200.0 401250.0 86400.0 400050.0 ; - RECT 85200.0 401250.0 86400.0 400050.0 ; - RECT 82800.0 401250.0 84000.0 400050.0 ; - RECT 87600.0 391350.0 88800.0 390150.0 ; - RECT 87600.0 401250.0 88800.0 400050.0 ; - RECT 83400.0 396600.0 84600.0 395400.0 ; - RECT 83400.0 396600.0 84600.0 395400.0 ; - RECT 85950.0 396450.0 86850.0 395550.0 ; - RECT 81000.0 389250.0 90600.0 388350.0 ; - RECT 81000.0 403050.0 90600.0 402150.0 ; - RECT 53250.0 395400.0 54450.0 396600.0 ; - RECT 55200.0 397800.0 56400.0 399000.0 ; - RECT 72000.0 396900.0 70800.0 398100.0 ; - RECT 63600.0 414450.0 64800.0 416400.0 ; - RECT 63600.0 402600.0 64800.0 404550.0 ; - RECT 58800.0 403950.0 60000.0 402150.0 ; - RECT 58800.0 413250.0 60000.0 416850.0 ; - RECT 61500.0 403950.0 62400.0 413250.0 ; - RECT 58800.0 413250.0 60000.0 414450.0 ; - RECT 61200.0 413250.0 62400.0 414450.0 ; - RECT 61200.0 413250.0 62400.0 414450.0 ; - RECT 58800.0 413250.0 60000.0 414450.0 ; - RECT 58800.0 403950.0 60000.0 405150.0 ; - RECT 61200.0 403950.0 62400.0 405150.0 ; - RECT 61200.0 403950.0 62400.0 405150.0 ; - RECT 58800.0 403950.0 60000.0 405150.0 ; - RECT 63600.0 413850.0 64800.0 415050.0 ; - RECT 63600.0 403950.0 64800.0 405150.0 ; - RECT 59400.0 408600.0 60600.0 409800.0 ; - RECT 59400.0 408600.0 60600.0 409800.0 ; - RECT 61950.0 408750.0 62850.0 409650.0 ; - RECT 57000.0 415950.0 66600.0 416850.0 ; - RECT 57000.0 402150.0 66600.0 403050.0 ; - RECT 68400.0 404550.0 69600.0 402150.0 ; - RECT 68400.0 413250.0 69600.0 416850.0 ; - RECT 73200.0 413250.0 74400.0 416850.0 ; - RECT 75600.0 414450.0 76800.0 416400.0 ; - RECT 75600.0 402600.0 76800.0 404550.0 ; - RECT 68400.0 413250.0 69600.0 414450.0 ; - RECT 70800.0 413250.0 72000.0 414450.0 ; - RECT 70800.0 413250.0 72000.0 414450.0 ; - RECT 68400.0 413250.0 69600.0 414450.0 ; - RECT 70800.0 413250.0 72000.0 414450.0 ; - RECT 73200.0 413250.0 74400.0 414450.0 ; - RECT 73200.0 413250.0 74400.0 414450.0 ; - RECT 70800.0 413250.0 72000.0 414450.0 ; - RECT 68400.0 404550.0 69600.0 405750.0 ; - RECT 70800.0 404550.0 72000.0 405750.0 ; - RECT 70800.0 404550.0 72000.0 405750.0 ; - RECT 68400.0 404550.0 69600.0 405750.0 ; - RECT 70800.0 404550.0 72000.0 405750.0 ; - RECT 73200.0 404550.0 74400.0 405750.0 ; - RECT 73200.0 404550.0 74400.0 405750.0 ; - RECT 70800.0 404550.0 72000.0 405750.0 ; - RECT 75600.0 413850.0 76800.0 415050.0 ; - RECT 75600.0 403950.0 76800.0 405150.0 ; - RECT 73200.0 407100.0 72000.0 408300.0 ; - RECT 70200.0 409800.0 69000.0 411000.0 ; - RECT 70800.0 413250.0 72000.0 414450.0 ; - RECT 73200.0 404550.0 74400.0 405750.0 ; - RECT 74400.0 409800.0 73200.0 411000.0 ; - RECT 69000.0 409800.0 70200.0 411000.0 ; - RECT 72000.0 407100.0 73200.0 408300.0 ; - RECT 73200.0 409800.0 74400.0 411000.0 ; - RECT 66600.0 415950.0 81000.0 416850.0 ; - RECT 66600.0 402150.0 81000.0 403050.0 ; - RECT 87600.0 414450.0 88800.0 416400.0 ; - RECT 87600.0 402600.0 88800.0 404550.0 ; - RECT 82800.0 403950.0 84000.0 402150.0 ; - RECT 82800.0 413250.0 84000.0 416850.0 ; - RECT 85500.0 403950.0 86400.0 413250.0 ; - RECT 82800.0 413250.0 84000.0 414450.0 ; - RECT 85200.0 413250.0 86400.0 414450.0 ; - RECT 85200.0 413250.0 86400.0 414450.0 ; - RECT 82800.0 413250.0 84000.0 414450.0 ; - RECT 82800.0 403950.0 84000.0 405150.0 ; - RECT 85200.0 403950.0 86400.0 405150.0 ; - RECT 85200.0 403950.0 86400.0 405150.0 ; - RECT 82800.0 403950.0 84000.0 405150.0 ; - RECT 87600.0 413850.0 88800.0 415050.0 ; - RECT 87600.0 403950.0 88800.0 405150.0 ; - RECT 83400.0 408600.0 84600.0 409800.0 ; - RECT 83400.0 408600.0 84600.0 409800.0 ; - RECT 85950.0 408750.0 86850.0 409650.0 ; - RECT 81000.0 415950.0 90600.0 416850.0 ; - RECT 81000.0 402150.0 90600.0 403050.0 ; - RECT 53250.0 408600.0 54450.0 409800.0 ; - RECT 55200.0 406200.0 56400.0 407400.0 ; - RECT 72000.0 407100.0 70800.0 408300.0 ; - RECT 63600.0 418350.0 64800.0 416400.0 ; - RECT 63600.0 430200.0 64800.0 428250.0 ; - RECT 58800.0 428850.0 60000.0 430650.0 ; - RECT 58800.0 419550.0 60000.0 415950.0 ; - RECT 61500.0 428850.0 62400.0 419550.0 ; - RECT 58800.0 419550.0 60000.0 418350.0 ; - RECT 61200.0 419550.0 62400.0 418350.0 ; - RECT 61200.0 419550.0 62400.0 418350.0 ; - RECT 58800.0 419550.0 60000.0 418350.0 ; - RECT 58800.0 428850.0 60000.0 427650.0 ; - RECT 61200.0 428850.0 62400.0 427650.0 ; - RECT 61200.0 428850.0 62400.0 427650.0 ; - RECT 58800.0 428850.0 60000.0 427650.0 ; - RECT 63600.0 418950.0 64800.0 417750.0 ; - RECT 63600.0 428850.0 64800.0 427650.0 ; - RECT 59400.0 424200.0 60600.0 423000.0 ; - RECT 59400.0 424200.0 60600.0 423000.0 ; - RECT 61950.0 424050.0 62850.0 423150.0 ; - RECT 57000.0 416850.0 66600.0 415950.0 ; - RECT 57000.0 430650.0 66600.0 429750.0 ; - RECT 68400.0 428250.0 69600.0 430650.0 ; - RECT 68400.0 419550.0 69600.0 415950.0 ; - RECT 73200.0 419550.0 74400.0 415950.0 ; - RECT 75600.0 418350.0 76800.0 416400.0 ; - RECT 75600.0 430200.0 76800.0 428250.0 ; - RECT 68400.0 419550.0 69600.0 418350.0 ; - RECT 70800.0 419550.0 72000.0 418350.0 ; - RECT 70800.0 419550.0 72000.0 418350.0 ; - RECT 68400.0 419550.0 69600.0 418350.0 ; - RECT 70800.0 419550.0 72000.0 418350.0 ; - RECT 73200.0 419550.0 74400.0 418350.0 ; - RECT 73200.0 419550.0 74400.0 418350.0 ; - RECT 70800.0 419550.0 72000.0 418350.0 ; - RECT 68400.0 428250.0 69600.0 427050.0 ; - RECT 70800.0 428250.0 72000.0 427050.0 ; - RECT 70800.0 428250.0 72000.0 427050.0 ; - RECT 68400.0 428250.0 69600.0 427050.0 ; - RECT 70800.0 428250.0 72000.0 427050.0 ; - RECT 73200.0 428250.0 74400.0 427050.0 ; - RECT 73200.0 428250.0 74400.0 427050.0 ; - RECT 70800.0 428250.0 72000.0 427050.0 ; - RECT 75600.0 418950.0 76800.0 417750.0 ; - RECT 75600.0 428850.0 76800.0 427650.0 ; - RECT 73200.0 425700.0 72000.0 424500.0 ; - RECT 70200.0 423000.0 69000.0 421800.0 ; - RECT 70800.0 419550.0 72000.0 418350.0 ; - RECT 73200.0 428250.0 74400.0 427050.0 ; - RECT 74400.0 423000.0 73200.0 421800.0 ; - RECT 69000.0 423000.0 70200.0 421800.0 ; - RECT 72000.0 425700.0 73200.0 424500.0 ; - RECT 73200.0 423000.0 74400.0 421800.0 ; - RECT 66600.0 416850.0 81000.0 415950.0 ; - RECT 66600.0 430650.0 81000.0 429750.0 ; - RECT 87600.0 418350.0 88800.0 416400.0 ; - RECT 87600.0 430200.0 88800.0 428250.0 ; - RECT 82800.0 428850.0 84000.0 430650.0 ; - RECT 82800.0 419550.0 84000.0 415950.0 ; - RECT 85500.0 428850.0 86400.0 419550.0 ; - RECT 82800.0 419550.0 84000.0 418350.0 ; - RECT 85200.0 419550.0 86400.0 418350.0 ; - RECT 85200.0 419550.0 86400.0 418350.0 ; - RECT 82800.0 419550.0 84000.0 418350.0 ; - RECT 82800.0 428850.0 84000.0 427650.0 ; - RECT 85200.0 428850.0 86400.0 427650.0 ; - RECT 85200.0 428850.0 86400.0 427650.0 ; - RECT 82800.0 428850.0 84000.0 427650.0 ; - RECT 87600.0 418950.0 88800.0 417750.0 ; - RECT 87600.0 428850.0 88800.0 427650.0 ; - RECT 83400.0 424200.0 84600.0 423000.0 ; - RECT 83400.0 424200.0 84600.0 423000.0 ; - RECT 85950.0 424050.0 86850.0 423150.0 ; - RECT 81000.0 416850.0 90600.0 415950.0 ; - RECT 81000.0 430650.0 90600.0 429750.0 ; - RECT 53250.0 423000.0 54450.0 424200.0 ; - RECT 55200.0 425400.0 56400.0 426600.0 ; - RECT 72000.0 424500.0 70800.0 425700.0 ; - RECT 50700.0 213150.0 55800.0 214050.0 ; - RECT 50700.0 232350.0 55800.0 233250.0 ; - RECT 50700.0 240750.0 55800.0 241650.0 ; - RECT 50700.0 259950.0 55800.0 260850.0 ; - RECT 50700.0 268350.0 55800.0 269250.0 ; - RECT 50700.0 287550.0 55800.0 288450.0 ; - RECT 50700.0 295950.0 55800.0 296850.0 ; - RECT 50700.0 315150.0 55800.0 316050.0 ; - RECT 50700.0 323550.0 55800.0 324450.0 ; - RECT 50700.0 342750.0 55800.0 343650.0 ; - RECT 50700.0 351150.0 55800.0 352050.0 ; - RECT 50700.0 370350.0 55800.0 371250.0 ; - RECT 50700.0 378750.0 55800.0 379650.0 ; - RECT 50700.0 397950.0 55800.0 398850.0 ; - RECT 50700.0 406350.0 55800.0 407250.0 ; - RECT 50700.0 425550.0 55800.0 426450.0 ; - RECT 85950.0 215550.0 86850.0 216450.0 ; - RECT 85950.0 229950.0 86850.0 230850.0 ; - RECT 85950.0 243150.0 86850.0 244050.0 ; - RECT 85950.0 257550.0 86850.0 258450.0 ; - RECT 85950.0 270750.0 86850.0 271650.0 ; - RECT 85950.0 285150.0 86850.0 286050.0 ; - RECT 85950.0 298350.0 86850.0 299250.0 ; - RECT 85950.0 312750.0 86850.0 313650.0 ; - RECT 85950.0 325950.0 86850.0 326850.0 ; - RECT 85950.0 340350.0 86850.0 341250.0 ; - RECT 85950.0 353550.0 86850.0 354450.0 ; - RECT 85950.0 367950.0 86850.0 368850.0 ; - RECT 85950.0 381150.0 86850.0 382050.0 ; - RECT 85950.0 395550.0 86850.0 396450.0 ; - RECT 85950.0 408750.0 86850.0 409650.0 ; - RECT 85950.0 423150.0 86850.0 424050.0 ; - RECT 50700.0 222750.0 57000.0 223650.0 ; - RECT 50700.0 250350.0 57000.0 251250.0 ; - RECT 50700.0 277950.0 57000.0 278850.0 ; - RECT 50700.0 305550.0 57000.0 306450.0 ; - RECT 50700.0 333150.0 57000.0 334050.0 ; - RECT 50700.0 360750.0 57000.0 361650.0 ; - RECT 50700.0 388350.0 57000.0 389250.0 ; - RECT 50700.0 415950.0 57000.0 416850.0 ; - RECT 50700.0 208950.0 57000.0 209850.0 ; - RECT 50700.0 236550.0 57000.0 237450.0 ; - RECT 50700.0 264150.0 57000.0 265050.0 ; - RECT 50700.0 291750.0 57000.0 292650.0 ; - RECT 50700.0 319350.0 57000.0 320250.0 ; - RECT 50700.0 346950.0 57000.0 347850.0 ; - RECT 50700.0 374550.0 57000.0 375450.0 ; - RECT 50700.0 402150.0 57000.0 403050.0 ; - RECT 50700.0 429750.0 57000.0 430650.0 ; - RECT 9900.0 93600.0 69900.0 83400.0 ; - RECT 9900.0 73200.0 69900.0 83400.0 ; - RECT 9900.0 73200.0 69900.0 63000.0 ; - RECT 9900.0 52800.0 69900.0 63000.0 ; - RECT 12300.0 93600.0 13200.0 52800.0 ; - RECT 66300.0 93600.0 67200.0 52800.0 ; - RECT 0.0 0.0 3600.0 3600.0 ; - RECT 0.0 453300.0 3600.0 456900.0 ; - RECT 139500.0 0.0 143100.0 3600.0 ; - RECT 139500.0 453300.0 143100.0 456900.0 ; - RECT 4950.0 4950.0 8550.0 8550.0 ; - RECT 4950.0 458250.0 8550.0 461850.0 ; - RECT 144450.0 4950.0 148050.0 8550.0 ; - RECT 144450.0 458250.0 148050.0 461850.0 ; - RECT 81300.0 101250.0 80100.0 102450.0 ; - RECT 86400.0 101100.0 85200.0 102300.0 ; - RECT 78300.0 115050.0 77100.0 116250.0 ; - RECT 89100.0 114900.0 87900.0 116100.0 ; - RECT 81300.0 156450.0 80100.0 157650.0 ; - RECT 91800.0 156300.0 90600.0 157500.0 ; - RECT 78300.0 170250.0 77100.0 171450.0 ; - RECT 94500.0 170100.0 93300.0 171300.0 ; - RECT 3600.0 98400.0 -5.3290705182e-12 99600.0 ; - RECT 3600.0 126000.0 -5.3290705182e-12 127200.0 ; - RECT 3600.0 153600.0 -5.3290705182e-12 154800.0 ; - RECT 3600.0 181200.0 -5.3290705182e-12 182400.0 ; - RECT 8550.0 112200.0 4950.0 113400.0 ; - RECT 8550.0 139800.0 4950.0 141000.0 ; - RECT 8550.0 167400.0 4950.0 168600.0 ; - RECT 8550.0 195000.0 4950.0 196200.0 ; - RECT 69300.0 87150.0 68100.0 88350.0 ; - RECT 86400.0 87150.0 85200.0 88350.0 ; - RECT 69300.0 78450.0 68100.0 79650.0 ; - RECT 89100.0 78450.0 87900.0 79650.0 ; - RECT 69300.0 66750.0 68100.0 67950.0 ; - RECT 91800.0 66750.0 90600.0 67950.0 ; - RECT 69300.0 58050.0 68100.0 59250.0 ; - RECT 94500.0 58050.0 93300.0 59250.0 ; - RECT 11100.0 82800.0 9900.0 84000.0 ; - RECT 3600.0 82800.0 -5.3290705182e-12 84000.0 ; - RECT 11100.0 62400.0 9900.0 63600.0 ; - RECT 3600.0 62400.0 -5.3290705182e-12 63600.0 ; - RECT 8550.0 50100.0 4950.0 51300.0 ; - RECT 105300.0 42150.0 104100.0 43350.0 ; - RECT 99900.0 37650.0 98700.0 38850.0 ; - RECT 102600.0 35250.0 101400.0 36450.0 ; - RECT 105300.0 438450.0 104100.0 439650.0 ; - RECT 108000.0 106950.0 106800.0 108150.0 ; - RECT 110700.0 205050.0 109500.0 206250.0 ; - RECT 97200.0 95100.0 96000.0 96300.0 ; - RECT 54450.0 431700.0 53250.0 432900.0 ; - RECT 97200.0 431700.0 96000.0 432900.0 ; - RECT 148050.0 449550.0 144450.0 450750.0 ; - RECT 148050.0 177750.0 144450.0 178950.0 ; - RECT 148050.0 109050.0 144450.0 110250.0 ; - RECT 148050.0 96150.0 144450.0 97350.0 ; - RECT 148050.0 19350.0 144450.0 20550.0 ; - RECT 8550.0 222600.0 4950.0 223800.0 ; - RECT 148050.0 222600.0 144450.0 223800.0 ; - RECT 8550.0 250200.0 4950.0 251400.0 ; - RECT 148050.0 250200.0 144450.0 251400.0 ; - RECT 8550.0 277800.0 4950.0 279000.0 ; - RECT 148050.0 277800.0 144450.0 279000.0 ; - RECT 8550.0 305400.0 4950.0 306600.0 ; - RECT 148050.0 305400.0 144450.0 306600.0 ; - RECT 8550.0 333000.0 4950.0 334200.0 ; - RECT 148050.0 333000.0 144450.0 334200.0 ; - RECT 8550.0 360600.0 4950.0 361800.0 ; - RECT 148050.0 360600.0 144450.0 361800.0 ; - RECT 8550.0 388200.0 4950.0 389400.0 ; - RECT 148050.0 388200.0 144450.0 389400.0 ; - RECT 8550.0 415800.0 4950.0 417000.0 ; - RECT 148050.0 415800.0 144450.0 417000.0 ; - RECT 143100.0 33150.0 139500.0 34350.0 ; - RECT 143100.0 202950.0 139500.0 204150.0 ; - RECT 143100.0 104850.0 139500.0 106050.0 ; - RECT 3600.0 208800.0 -5.3290705182e-12 210000.0 ; - RECT 3600.0 236400.0 -5.3290705182e-12 237600.0 ; - RECT 3600.0 264000.0 -5.3290705182e-12 265200.0 ; - RECT 3600.0 291600.0 -5.3290705182e-12 292800.0 ; - RECT 3600.0 319200.0 -5.3290705182e-12 320400.0 ; - RECT 3600.0 346800.0 -5.3290705182e-12 348000.0 ; - RECT 3600.0 374400.0 -5.3290705182e-12 375600.0 ; - RECT 3600.0 402000.0 -5.3290705182e-12 403200.0 ; - RECT 3600.0 429600.0 -5.3290705182e-12 430800.0 ; - RECT 0.0 4950.0 148050.0 8550.0 ; - RECT 0.0 458250.0 148050.0 461850.0 ; - RECT 0.0 0.0 148050.0 3600.0 ; - RECT 0.0 453300.0 148050.0 456900.0 ; - RECT -9150.0 187200.0 -10050.0 196800.0 ; - RECT -9000.0 203400.0 -9900.0 204300.0 ; - RECT -9450.0 203400.0 -9600.0 204300.0 ; - RECT -9000.0 203850.0 -9900.0 211200.0 ; - RECT -9000.0 223050.0 -9900.0 230400.0 ; - RECT -17250.0 238200.0 -22200.0 239100.0 ; - RECT -9150.0 186750.0 -10050.0 187650.0 ; - RECT -9150.0 203400.0 -10050.0 204300.0 ; - RECT -23550.0 341700.0 -24450.0 355050.0 ; - RECT -9000.0 252300.0 -9900.0 264450.0 ; - RECT -19500.0 184200.0 -22200.0 185100.0 ; - RECT -23100.0 264450.0 -24000.0 291300.0 ; - RECT -25800.0 269850.0 -26700.0 294300.0 ; - RECT -11100.0 283350.0 -12000.0 291900.0 ; - RECT -9150.0 280650.0 -10050.0 294300.0 ; - RECT -7200.0 272550.0 -8100.0 296700.0 ; - RECT -11100.0 306450.0 -12000.0 307350.0 ; - RECT -11100.0 297900.0 -12000.0 306900.0 ; - RECT -9600.0 306450.0 -11550.0 307350.0 ; - RECT -9000.0 308850.0 -9900.0 309750.0 ; - RECT -9450.0 308850.0 -9600.0 309750.0 ; - RECT -9000.0 309300.0 -9900.0 366900.0 ; - RECT -38700.0 283350.0 -39600.0 301500.0 ; - RECT -36750.0 272550.0 -37650.0 303900.0 ; - RECT -34800.0 275250.0 -35700.0 306300.0 ; - RECT -38700.0 316050.0 -39600.0 316950.0 ; - RECT -38700.0 307500.0 -39600.0 316500.0 ; - RECT -37200.0 316050.0 -39150.0 316950.0 ; - RECT -36750.0 318900.0 -37650.0 326100.0 ; - RECT -36750.0 328500.0 -37650.0 335700.0 ; - RECT -23550.0 341250.0 -24450.0 342150.0 ; - RECT -24000.0 341250.0 -24450.0 342150.0 ; - RECT -23550.0 339300.0 -24450.0 341700.0 ; - RECT -23550.0 329100.0 -24450.0 336300.0 ; - RECT -23100.0 296400.0 -24000.0 302700.0 ; - RECT -22350.0 312600.0 -23250.0 319800.0 ; - RECT -36750.0 338100.0 -37650.0 342300.0 ; - RECT -23550.0 322500.0 -24450.0 326700.0 ; - RECT -2550.0 181800.0 -3450.0 341700.0 ; - RECT -2550.0 267150.0 -3450.0 288300.0 ; - RECT -16350.0 181800.0 -17250.0 341700.0 ; - RECT -16350.0 277950.0 -17250.0 288300.0 ; - RECT -30150.0 288300.0 -31050.0 341700.0 ; - RECT -30150.0 267150.0 -31050.0 288300.0 ; - RECT -43950.0 288300.0 -44850.0 341700.0 ; - RECT -43950.0 277950.0 -44850.0 288300.0 ; - RECT -43950.0 341250.0 -44850.0 342150.0 ; - RECT -43950.0 339600.0 -44850.0 341700.0 ; - RECT -44400.0 341250.0 -49200.0 342150.0 ; - RECT -52800.0 181800.0 -42600.0 241800.0 ; - RECT -32400.0 181800.0 -42600.0 241800.0 ; - RECT -32400.0 181800.0 -22200.0 241800.0 ; - RECT -52800.0 184200.0 -22200.0 185100.0 ; - RECT -52800.0 238200.0 -22200.0 239100.0 ; - RECT -14850.0 190800.0 -16800.0 192000.0 ; - RECT -3000.0 190800.0 -4950.0 192000.0 ; - RECT -4350.0 186300.0 -13650.0 187200.0 ; - RECT -14250.0 183750.0 -16200.0 184650.0 ; - RECT -14250.0 188550.0 -16200.0 189450.0 ; - RECT -13650.0 183600.0 -14850.0 184800.0 ; - RECT -13650.0 188400.0 -14850.0 189600.0 ; - RECT -13650.0 186000.0 -14850.0 187200.0 ; - RECT -13650.0 186000.0 -14850.0 187200.0 ; - RECT -15750.0 183750.0 -16650.0 189450.0 ; - RECT -3000.0 183750.0 -4950.0 184650.0 ; - RECT -3000.0 188550.0 -4950.0 189450.0 ; - RECT -4350.0 183600.0 -5550.0 184800.0 ; - RECT -4350.0 188400.0 -5550.0 189600.0 ; - RECT -4350.0 186000.0 -5550.0 187200.0 ; - RECT -4350.0 186000.0 -5550.0 187200.0 ; - RECT -2550.0 183750.0 -3450.0 189450.0 ; - RECT -14250.0 190800.0 -15450.0 192000.0 ; - RECT -4350.0 190800.0 -5550.0 192000.0 ; - RECT -9000.0 184200.0 -10200.0 185400.0 ; - RECT -9000.0 184200.0 -10200.0 185400.0 ; - RECT -9150.0 186750.0 -10050.0 187650.0 ; - RECT -16350.0 181800.0 -17250.0 193800.0 ; - RECT -2550.0 181800.0 -3450.0 193800.0 ; - RECT -14850.0 205200.0 -16800.0 206400.0 ; - RECT -3000.0 205200.0 -4950.0 206400.0 ; - RECT -15450.0 195750.0 -17250.0 201450.0 ; - RECT -6750.0 202950.0 -11550.0 203850.0 ; - RECT -13950.0 195750.0 -15900.0 196650.0 ; - RECT -13950.0 200550.0 -15900.0 201450.0 ; - RECT -12000.0 198150.0 -13950.0 199050.0 ; - RECT -12000.0 202950.0 -13950.0 203850.0 ; - RECT -13350.0 195600.0 -14550.0 196800.0 ; - RECT -13350.0 200400.0 -14550.0 201600.0 ; - RECT -13350.0 198000.0 -14550.0 199200.0 ; - RECT -13350.0 202800.0 -14550.0 204000.0 ; - RECT -11550.0 198150.0 -12450.0 203850.0 ; - RECT -15450.0 195750.0 -16350.0 201450.0 ; - RECT -3300.0 195750.0 -5250.0 196650.0 ; - RECT -3300.0 200550.0 -5250.0 201450.0 ; - RECT -5250.0 198150.0 -7200.0 199050.0 ; - RECT -5250.0 202950.0 -7200.0 203850.0 ; - RECT -4650.0 195600.0 -5850.0 196800.0 ; - RECT -4650.0 200400.0 -5850.0 201600.0 ; - RECT -4650.0 198000.0 -5850.0 199200.0 ; - RECT -4650.0 202800.0 -5850.0 204000.0 ; - RECT -6750.0 198150.0 -7650.0 203850.0 ; - RECT -2850.0 195750.0 -3750.0 201450.0 ; - RECT -14250.0 205200.0 -15450.0 206400.0 ; - RECT -4350.0 205200.0 -5550.0 206400.0 ; - RECT -9000.0 196200.0 -10200.0 197400.0 ; - RECT -9000.0 196200.0 -10200.0 197400.0 ; - RECT -9150.0 203400.0 -10050.0 204300.0 ; - RECT -16350.0 193800.0 -17250.0 208200.0 ; - RECT -2550.0 193800.0 -3450.0 208200.0 ; - RECT -14850.0 224400.0 -16800.0 225600.0 ; - RECT -3000.0 224400.0 -4950.0 225600.0 ; - RECT -15000.0 210150.0 -17250.0 220650.0 ; - RECT -6900.0 222150.0 -11100.0 223050.0 ; - RECT -13500.0 210150.0 -15450.0 211050.0 ; - RECT -13500.0 214950.0 -15450.0 215850.0 ; - RECT -13500.0 219750.0 -15450.0 220650.0 ; - RECT -11550.0 212550.0 -13500.0 213450.0 ; - RECT -11550.0 217350.0 -13500.0 218250.0 ; - RECT -11550.0 222150.0 -13500.0 223050.0 ; - RECT -12900.0 210000.0 -14100.0 211200.0 ; - RECT -12900.0 214800.0 -14100.0 216000.0 ; - RECT -12900.0 219600.0 -14100.0 220800.0 ; - RECT -12900.0 212400.0 -14100.0 213600.0 ; - RECT -12900.0 217200.0 -14100.0 218400.0 ; - RECT -12900.0 222000.0 -14100.0 223200.0 ; - RECT -11100.0 212550.0 -12000.0 223050.0 ; - RECT -15000.0 210150.0 -15900.0 220650.0 ; - RECT -3450.0 210150.0 -5400.0 211050.0 ; - RECT -3450.0 214950.0 -5400.0 215850.0 ; - RECT -3450.0 219750.0 -5400.0 220650.0 ; - RECT -5400.0 212550.0 -7350.0 213450.0 ; - RECT -5400.0 217350.0 -7350.0 218250.0 ; - RECT -5400.0 222150.0 -7350.0 223050.0 ; - RECT -4800.0 210000.0 -6000.0 211200.0 ; - RECT -4800.0 214800.0 -6000.0 216000.0 ; - RECT -4800.0 219600.0 -6000.0 220800.0 ; - RECT -4800.0 212400.0 -6000.0 213600.0 ; - RECT -4800.0 217200.0 -6000.0 218400.0 ; - RECT -4800.0 222000.0 -6000.0 223200.0 ; - RECT -6900.0 212550.0 -7800.0 223050.0 ; - RECT -3000.0 210150.0 -3900.0 220650.0 ; - RECT -14250.0 224400.0 -15450.0 225600.0 ; - RECT -4350.0 224400.0 -5550.0 225600.0 ; - RECT -8850.0 210600.0 -10050.0 211800.0 ; - RECT -8850.0 210600.0 -10050.0 211800.0 ; - RECT -9000.0 222600.0 -9900.0 223500.0 ; - RECT -16350.0 208200.0 -17250.0 227400.0 ; - RECT -2550.0 208200.0 -3450.0 227400.0 ; - RECT -14850.0 255600.0 -16800.0 256800.0 ; - RECT -3000.0 255600.0 -4950.0 256800.0 ; - RECT -15000.0 229350.0 -17250.0 254250.0 ; - RECT -6900.0 250950.0 -11100.0 251850.0 ; - RECT -13500.0 229350.0 -15450.0 230250.0 ; - RECT -13500.0 234150.0 -15450.0 235050.0 ; - RECT -13500.0 238950.0 -15450.0 239850.0 ; - RECT -13500.0 243750.0 -15450.0 244650.0 ; - RECT -13500.0 248550.0 -15450.0 249450.0 ; - RECT -13500.0 253350.0 -15450.0 254250.0 ; - RECT -11550.0 231750.0 -13500.0 232650.0 ; - RECT -11550.0 236550.0 -13500.0 237450.0 ; - RECT -11550.0 241350.0 -13500.0 242250.0 ; - RECT -11550.0 246150.0 -13500.0 247050.0 ; - RECT -11550.0 250950.0 -13500.0 251850.0 ; - RECT -12900.0 229200.0 -14100.0 230400.0 ; - RECT -12900.0 234000.0 -14100.0 235200.0 ; - RECT -12900.0 238800.0 -14100.0 240000.0 ; - RECT -12900.0 243600.0 -14100.0 244800.0 ; - RECT -12900.0 248400.0 -14100.0 249600.0 ; - RECT -12900.0 253200.0 -14100.0 254400.0 ; - RECT -12900.0 231600.0 -14100.0 232800.0 ; - RECT -12900.0 236400.0 -14100.0 237600.0 ; - RECT -12900.0 241200.0 -14100.0 242400.0 ; - RECT -12900.0 246000.0 -14100.0 247200.0 ; - RECT -12900.0 250800.0 -14100.0 252000.0 ; - RECT -11100.0 231750.0 -12000.0 251850.0 ; - RECT -15000.0 229350.0 -15900.0 254250.0 ; - RECT -3450.0 229350.0 -5400.0 230250.0 ; - RECT -3450.0 234150.0 -5400.0 235050.0 ; - RECT -3450.0 238950.0 -5400.0 239850.0 ; - RECT -3450.0 243750.0 -5400.0 244650.0 ; - RECT -3450.0 248550.0 -5400.0 249450.0 ; - RECT -3450.0 253350.0 -5400.0 254250.0 ; - RECT -5400.0 231750.0 -7350.0 232650.0 ; - RECT -5400.0 236550.0 -7350.0 237450.0 ; - RECT -5400.0 241350.0 -7350.0 242250.0 ; - RECT -5400.0 246150.0 -7350.0 247050.0 ; - RECT -5400.0 250950.0 -7350.0 251850.0 ; - RECT -4800.0 229200.0 -6000.0 230400.0 ; - RECT -4800.0 234000.0 -6000.0 235200.0 ; - RECT -4800.0 238800.0 -6000.0 240000.0 ; - RECT -4800.0 243600.0 -6000.0 244800.0 ; - RECT -4800.0 248400.0 -6000.0 249600.0 ; - RECT -4800.0 253200.0 -6000.0 254400.0 ; - RECT -4800.0 231600.0 -6000.0 232800.0 ; - RECT -4800.0 236400.0 -6000.0 237600.0 ; - RECT -4800.0 241200.0 -6000.0 242400.0 ; - RECT -4800.0 246000.0 -6000.0 247200.0 ; - RECT -4800.0 250800.0 -6000.0 252000.0 ; - RECT -6900.0 231750.0 -7800.0 251850.0 ; - RECT -3000.0 229350.0 -3900.0 254250.0 ; - RECT -14250.0 255600.0 -15450.0 256800.0 ; - RECT -4350.0 255600.0 -5550.0 256800.0 ; - RECT -8850.0 229800.0 -10050.0 231000.0 ; - RECT -8850.0 229800.0 -10050.0 231000.0 ; - RECT -9000.0 251400.0 -9900.0 252300.0 ; - RECT -16350.0 227400.0 -17250.0 258600.0 ; - RECT -2550.0 227400.0 -3450.0 258600.0 ; - RECT -4950.0 290100.0 -2550.0 291300.0 ; - RECT -13650.0 290100.0 -17250.0 291300.0 ; - RECT -13650.0 294900.0 -17250.0 296100.0 ; - RECT -14850.0 299700.0 -16800.0 300900.0 ; - RECT -3000.0 299700.0 -4950.0 300900.0 ; - RECT -13650.0 290100.0 -14850.0 291300.0 ; - RECT -13650.0 292500.0 -14850.0 293700.0 ; - RECT -13650.0 292500.0 -14850.0 293700.0 ; - RECT -13650.0 290100.0 -14850.0 291300.0 ; - RECT -13650.0 292500.0 -14850.0 293700.0 ; - RECT -13650.0 294900.0 -14850.0 296100.0 ; - RECT -13650.0 294900.0 -14850.0 296100.0 ; - RECT -13650.0 292500.0 -14850.0 293700.0 ; - RECT -13650.0 294900.0 -14850.0 296100.0 ; - RECT -13650.0 297300.0 -14850.0 298500.0 ; - RECT -13650.0 297300.0 -14850.0 298500.0 ; - RECT -13650.0 294900.0 -14850.0 296100.0 ; - RECT -4950.0 290100.0 -6150.0 291300.0 ; - RECT -4950.0 292500.0 -6150.0 293700.0 ; - RECT -4950.0 292500.0 -6150.0 293700.0 ; - RECT -4950.0 290100.0 -6150.0 291300.0 ; - RECT -4950.0 292500.0 -6150.0 293700.0 ; - RECT -4950.0 294900.0 -6150.0 296100.0 ; - RECT -4950.0 294900.0 -6150.0 296100.0 ; - RECT -4950.0 292500.0 -6150.0 293700.0 ; - RECT -4950.0 294900.0 -6150.0 296100.0 ; - RECT -4950.0 297300.0 -6150.0 298500.0 ; - RECT -4950.0 297300.0 -6150.0 298500.0 ; - RECT -4950.0 294900.0 -6150.0 296100.0 ; - RECT -14250.0 299700.0 -15450.0 300900.0 ; - RECT -4350.0 299700.0 -5550.0 300900.0 ; - RECT -7050.0 297300.0 -8250.0 296100.0 ; - RECT -9000.0 294900.0 -10200.0 293700.0 ; - RECT -10950.0 292500.0 -12150.0 291300.0 ; - RECT -13650.0 292500.0 -14850.0 293700.0 ; - RECT -13650.0 297300.0 -14850.0 298500.0 ; - RECT -4950.0 297300.0 -6150.0 298500.0 ; - RECT -10950.0 297300.0 -12150.0 298500.0 ; - RECT -10950.0 291300.0 -12150.0 292500.0 ; - RECT -9000.0 293700.0 -10200.0 294900.0 ; - RECT -7050.0 296100.0 -8250.0 297300.0 ; - RECT -10950.0 297300.0 -12150.0 298500.0 ; - RECT -16350.0 288300.0 -17250.0 303900.0 ; - RECT -2550.0 288300.0 -3450.0 303900.0 ; - RECT -14850.0 310500.0 -16800.0 311700.0 ; - RECT -3000.0 310500.0 -4950.0 311700.0 ; - RECT -4350.0 305700.0 -2550.0 306900.0 ; - RECT -13650.0 305700.0 -17250.0 306900.0 ; - RECT -4350.0 308400.0 -13650.0 309300.0 ; - RECT -13650.0 305700.0 -14850.0 306900.0 ; - RECT -13650.0 308100.0 -14850.0 309300.0 ; - RECT -13650.0 308100.0 -14850.0 309300.0 ; - RECT -13650.0 305700.0 -14850.0 306900.0 ; - RECT -4350.0 305700.0 -5550.0 306900.0 ; - RECT -4350.0 308100.0 -5550.0 309300.0 ; - RECT -4350.0 308100.0 -5550.0 309300.0 ; - RECT -4350.0 305700.0 -5550.0 306900.0 ; - RECT -14250.0 310500.0 -15450.0 311700.0 ; - RECT -4350.0 310500.0 -5550.0 311700.0 ; - RECT -9000.0 306300.0 -10200.0 307500.0 ; - RECT -9000.0 306300.0 -10200.0 307500.0 ; - RECT -9150.0 308850.0 -10050.0 309750.0 ; - RECT -16350.0 303900.0 -17250.0 313500.0 ; - RECT -2550.0 303900.0 -3450.0 313500.0 ; - RECT -29250.0 290100.0 -31050.0 291300.0 ; - RECT -29250.0 294900.0 -31050.0 296100.0 ; - RECT -20550.0 290100.0 -16350.0 291300.0 ; - RECT -18750.0 297300.0 -16800.0 298500.0 ; - RECT -30600.0 297300.0 -28650.0 298500.0 ; - RECT -20550.0 290100.0 -19350.0 291300.0 ; - RECT -20550.0 292500.0 -19350.0 293700.0 ; - RECT -20550.0 292500.0 -19350.0 293700.0 ; - RECT -20550.0 290100.0 -19350.0 291300.0 ; - RECT -20550.0 292500.0 -19350.0 293700.0 ; - RECT -20550.0 294900.0 -19350.0 296100.0 ; - RECT -20550.0 294900.0 -19350.0 296100.0 ; - RECT -20550.0 292500.0 -19350.0 293700.0 ; - RECT -29250.0 290100.0 -28050.0 291300.0 ; - RECT -29250.0 292500.0 -28050.0 293700.0 ; - RECT -29250.0 292500.0 -28050.0 293700.0 ; - RECT -29250.0 290100.0 -28050.0 291300.0 ; - RECT -29250.0 292500.0 -28050.0 293700.0 ; - RECT -29250.0 294900.0 -28050.0 296100.0 ; - RECT -29250.0 294900.0 -28050.0 296100.0 ; - RECT -29250.0 292500.0 -28050.0 293700.0 ; - RECT -19350.0 297300.0 -18150.0 298500.0 ; - RECT -29250.0 297300.0 -28050.0 298500.0 ; - RECT -26850.0 294900.0 -25650.0 293700.0 ; - RECT -24150.0 291900.0 -22950.0 290700.0 ; - RECT -20550.0 294900.0 -19350.0 296100.0 ; - RECT -29250.0 293700.0 -28050.0 292500.0 ; - RECT -24150.0 297000.0 -22950.0 295800.0 ; - RECT -24150.0 290700.0 -22950.0 291900.0 ; - RECT -26850.0 293700.0 -25650.0 294900.0 ; - RECT -24150.0 295800.0 -22950.0 297000.0 ; - RECT -17250.0 288300.0 -16350.0 302700.0 ; - RECT -31050.0 288300.0 -30150.0 302700.0 ; - RECT -28650.0 307200.0 -31050.0 308400.0 ; - RECT -19950.0 307200.0 -16350.0 308400.0 ; - RECT -19950.0 312000.0 -16350.0 313200.0 ; - RECT -18750.0 314400.0 -16800.0 315600.0 ; - RECT -30600.0 314400.0 -28650.0 315600.0 ; - RECT -19950.0 307200.0 -18750.0 308400.0 ; - RECT -19950.0 309600.0 -18750.0 310800.0 ; - RECT -19950.0 309600.0 -18750.0 310800.0 ; - RECT -19950.0 307200.0 -18750.0 308400.0 ; - RECT -19950.0 309600.0 -18750.0 310800.0 ; - RECT -19950.0 312000.0 -18750.0 313200.0 ; - RECT -19950.0 312000.0 -18750.0 313200.0 ; - RECT -19950.0 309600.0 -18750.0 310800.0 ; - RECT -28650.0 307200.0 -27450.0 308400.0 ; - RECT -28650.0 309600.0 -27450.0 310800.0 ; - RECT -28650.0 309600.0 -27450.0 310800.0 ; - RECT -28650.0 307200.0 -27450.0 308400.0 ; - RECT -28650.0 309600.0 -27450.0 310800.0 ; - RECT -28650.0 312000.0 -27450.0 313200.0 ; - RECT -28650.0 312000.0 -27450.0 313200.0 ; - RECT -28650.0 309600.0 -27450.0 310800.0 ; - RECT -19350.0 314400.0 -18150.0 315600.0 ; - RECT -29250.0 314400.0 -28050.0 315600.0 ; - RECT -26100.0 312000.0 -24900.0 310800.0 ; - RECT -23400.0 309000.0 -22200.0 307800.0 ; - RECT -19950.0 309600.0 -18750.0 310800.0 ; - RECT -28650.0 312000.0 -27450.0 313200.0 ; - RECT -23400.0 313200.0 -22200.0 312000.0 ; - RECT -23400.0 307800.0 -22200.0 309000.0 ; - RECT -26100.0 310800.0 -24900.0 312000.0 ; - RECT -23400.0 312000.0 -22200.0 313200.0 ; - RECT -17250.0 305400.0 -16350.0 319800.0 ; - RECT -31050.0 305400.0 -30150.0 319800.0 ; - RECT -18750.0 325500.0 -16800.0 324300.0 ; - RECT -30600.0 325500.0 -28650.0 324300.0 ; - RECT -29250.0 330300.0 -31050.0 329100.0 ; - RECT -19950.0 330300.0 -16350.0 329100.0 ; - RECT -29250.0 327600.0 -19950.0 326700.0 ; - RECT -19950.0 330300.0 -18750.0 329100.0 ; - RECT -19950.0 327900.0 -18750.0 326700.0 ; - RECT -19950.0 327900.0 -18750.0 326700.0 ; - RECT -19950.0 330300.0 -18750.0 329100.0 ; - RECT -29250.0 330300.0 -28050.0 329100.0 ; - RECT -29250.0 327900.0 -28050.0 326700.0 ; - RECT -29250.0 327900.0 -28050.0 326700.0 ; - RECT -29250.0 330300.0 -28050.0 329100.0 ; - RECT -19350.0 325500.0 -18150.0 324300.0 ; - RECT -29250.0 325500.0 -28050.0 324300.0 ; - RECT -24600.0 329700.0 -23400.0 328500.0 ; - RECT -24600.0 329700.0 -23400.0 328500.0 ; - RECT -24450.0 327150.0 -23550.0 326250.0 ; - RECT -17250.0 332100.0 -16350.0 322500.0 ; - RECT -31050.0 332100.0 -30150.0 322500.0 ; - RECT -18750.0 335100.0 -16800.0 333900.0 ; - RECT -30600.0 335100.0 -28650.0 333900.0 ; - RECT -29250.0 339900.0 -31050.0 338700.0 ; - RECT -19950.0 339900.0 -16350.0 338700.0 ; - RECT -29250.0 337200.0 -19950.0 336300.0 ; - RECT -19950.0 339900.0 -18750.0 338700.0 ; - RECT -19950.0 337500.0 -18750.0 336300.0 ; - RECT -19950.0 337500.0 -18750.0 336300.0 ; - RECT -19950.0 339900.0 -18750.0 338700.0 ; - RECT -29250.0 339900.0 -28050.0 338700.0 ; - RECT -29250.0 337500.0 -28050.0 336300.0 ; - RECT -29250.0 337500.0 -28050.0 336300.0 ; - RECT -29250.0 339900.0 -28050.0 338700.0 ; - RECT -19350.0 335100.0 -18150.0 333900.0 ; - RECT -29250.0 335100.0 -28050.0 333900.0 ; - RECT -24600.0 339300.0 -23400.0 338100.0 ; - RECT -24600.0 339300.0 -23400.0 338100.0 ; - RECT -24450.0 336750.0 -23550.0 335850.0 ; - RECT -17250.0 341700.0 -16350.0 332100.0 ; - RECT -31050.0 341700.0 -30150.0 332100.0 ; - RECT -32550.0 299700.0 -30150.0 300900.0 ; - RECT -41250.0 299700.0 -44850.0 300900.0 ; - RECT -41250.0 304500.0 -44850.0 305700.0 ; - RECT -42450.0 309300.0 -44400.0 310500.0 ; - RECT -30600.0 309300.0 -32550.0 310500.0 ; - RECT -41250.0 299700.0 -42450.0 300900.0 ; - RECT -41250.0 302100.0 -42450.0 303300.0 ; - RECT -41250.0 302100.0 -42450.0 303300.0 ; - RECT -41250.0 299700.0 -42450.0 300900.0 ; - RECT -41250.0 302100.0 -42450.0 303300.0 ; - RECT -41250.0 304500.0 -42450.0 305700.0 ; - RECT -41250.0 304500.0 -42450.0 305700.0 ; - RECT -41250.0 302100.0 -42450.0 303300.0 ; - RECT -41250.0 304500.0 -42450.0 305700.0 ; - RECT -41250.0 306900.0 -42450.0 308100.0 ; - RECT -41250.0 306900.0 -42450.0 308100.0 ; - RECT -41250.0 304500.0 -42450.0 305700.0 ; - RECT -32550.0 299700.0 -33750.0 300900.0 ; - RECT -32550.0 302100.0 -33750.0 303300.0 ; - RECT -32550.0 302100.0 -33750.0 303300.0 ; - RECT -32550.0 299700.0 -33750.0 300900.0 ; - RECT -32550.0 302100.0 -33750.0 303300.0 ; - RECT -32550.0 304500.0 -33750.0 305700.0 ; - RECT -32550.0 304500.0 -33750.0 305700.0 ; - RECT -32550.0 302100.0 -33750.0 303300.0 ; - RECT -32550.0 304500.0 -33750.0 305700.0 ; - RECT -32550.0 306900.0 -33750.0 308100.0 ; - RECT -32550.0 306900.0 -33750.0 308100.0 ; - RECT -32550.0 304500.0 -33750.0 305700.0 ; - RECT -41850.0 309300.0 -43050.0 310500.0 ; - RECT -31950.0 309300.0 -33150.0 310500.0 ; - RECT -34650.0 306900.0 -35850.0 305700.0 ; - RECT -36600.0 304500.0 -37800.0 303300.0 ; - RECT -38550.0 302100.0 -39750.0 300900.0 ; - RECT -41250.0 302100.0 -42450.0 303300.0 ; - RECT -41250.0 306900.0 -42450.0 308100.0 ; - RECT -32550.0 306900.0 -33750.0 308100.0 ; - RECT -38550.0 306900.0 -39750.0 308100.0 ; - RECT -38550.0 300900.0 -39750.0 302100.0 ; - RECT -36600.0 303300.0 -37800.0 304500.0 ; - RECT -34650.0 305700.0 -35850.0 306900.0 ; - RECT -38550.0 306900.0 -39750.0 308100.0 ; - RECT -43950.0 297900.0 -44850.0 313500.0 ; - RECT -30150.0 297900.0 -31050.0 313500.0 ; - RECT -42450.0 320100.0 -44400.0 321300.0 ; - RECT -30600.0 320100.0 -32550.0 321300.0 ; - RECT -31950.0 315300.0 -30150.0 316500.0 ; - RECT -41250.0 315300.0 -44850.0 316500.0 ; - RECT -31950.0 318000.0 -41250.0 318900.0 ; - RECT -41250.0 315300.0 -42450.0 316500.0 ; - RECT -41250.0 317700.0 -42450.0 318900.0 ; - RECT -41250.0 317700.0 -42450.0 318900.0 ; - RECT -41250.0 315300.0 -42450.0 316500.0 ; - RECT -31950.0 315300.0 -33150.0 316500.0 ; - RECT -31950.0 317700.0 -33150.0 318900.0 ; - RECT -31950.0 317700.0 -33150.0 318900.0 ; - RECT -31950.0 315300.0 -33150.0 316500.0 ; - RECT -41850.0 320100.0 -43050.0 321300.0 ; - RECT -31950.0 320100.0 -33150.0 321300.0 ; - RECT -36600.0 315900.0 -37800.0 317100.0 ; - RECT -36600.0 315900.0 -37800.0 317100.0 ; - RECT -36750.0 318450.0 -37650.0 319350.0 ; - RECT -43950.0 313500.0 -44850.0 323100.0 ; - RECT -30150.0 313500.0 -31050.0 323100.0 ; - RECT -42450.0 329700.0 -44400.0 330900.0 ; - RECT -30600.0 329700.0 -32550.0 330900.0 ; - RECT -31950.0 324900.0 -30150.0 326100.0 ; - RECT -41250.0 324900.0 -44850.0 326100.0 ; - RECT -31950.0 327600.0 -41250.0 328500.0 ; - RECT -41250.0 324900.0 -42450.0 326100.0 ; - RECT -41250.0 327300.0 -42450.0 328500.0 ; - RECT -41250.0 327300.0 -42450.0 328500.0 ; - RECT -41250.0 324900.0 -42450.0 326100.0 ; - RECT -31950.0 324900.0 -33150.0 326100.0 ; - RECT -31950.0 327300.0 -33150.0 328500.0 ; - RECT -31950.0 327300.0 -33150.0 328500.0 ; - RECT -31950.0 324900.0 -33150.0 326100.0 ; - RECT -41850.0 329700.0 -43050.0 330900.0 ; - RECT -31950.0 329700.0 -33150.0 330900.0 ; - RECT -36600.0 325500.0 -37800.0 326700.0 ; - RECT -36600.0 325500.0 -37800.0 326700.0 ; - RECT -36750.0 328050.0 -37650.0 328950.0 ; - RECT -43950.0 323100.0 -44850.0 332700.0 ; - RECT -30150.0 323100.0 -31050.0 332700.0 ; - RECT -42450.0 339300.0 -44400.0 340500.0 ; - RECT -30600.0 339300.0 -32550.0 340500.0 ; - RECT -31950.0 334500.0 -30150.0 335700.0 ; - RECT -41250.0 334500.0 -44850.0 335700.0 ; - RECT -31950.0 337200.0 -41250.0 338100.0 ; - RECT -41250.0 334500.0 -42450.0 335700.0 ; - RECT -41250.0 336900.0 -42450.0 338100.0 ; - RECT -41250.0 336900.0 -42450.0 338100.0 ; - RECT -41250.0 334500.0 -42450.0 335700.0 ; - RECT -31950.0 334500.0 -33150.0 335700.0 ; - RECT -31950.0 336900.0 -33150.0 338100.0 ; - RECT -31950.0 336900.0 -33150.0 338100.0 ; - RECT -31950.0 334500.0 -33150.0 335700.0 ; - RECT -41850.0 339300.0 -43050.0 340500.0 ; - RECT -31950.0 339300.0 -33150.0 340500.0 ; - RECT -36600.0 335100.0 -37800.0 336300.0 ; - RECT -36600.0 335100.0 -37800.0 336300.0 ; - RECT -36750.0 337650.0 -37650.0 338550.0 ; - RECT -43950.0 332700.0 -44850.0 342300.0 ; - RECT -30150.0 332700.0 -31050.0 342300.0 ; - RECT -30150.0 459150.0 -31050.0 437100.0 ; - RECT -31050.0 376350.0 -35400.0 377250.0 ; - RECT -31050.0 399750.0 -35400.0 400650.0 ; - RECT -31050.0 403950.0 -35400.0 404850.0 ; - RECT -31050.0 427350.0 -35400.0 428250.0 ; - RECT -30150.0 350850.0 -36000.0 351750.0 ; - RECT -36000.0 350850.0 -46200.0 351750.0 ; - RECT -48300.0 387900.0 -36000.0 388800.0 ; - RECT -48300.0 415500.0 -36000.0 416400.0 ; - RECT -48300.0 360300.0 -36000.0 361200.0 ; - RECT -23550.0 377100.0 -24450.0 389700.0 ; - RECT -23550.0 372150.0 -24450.0 373050.0 ; - RECT -23550.0 372600.0 -24450.0 377100.0 ; - RECT -24000.0 372150.0 -35400.0 373050.0 ; - RECT -16800.0 377850.0 -19050.0 378750.0 ; - RECT -19200.0 363150.0 -20100.0 364050.0 ; - RECT -23550.0 363150.0 -24450.0 364050.0 ; - RECT -19200.0 363600.0 -20100.0 375300.0 ; - RECT -19650.0 363150.0 -24000.0 364050.0 ; - RECT -23550.0 358500.0 -24450.0 363600.0 ; - RECT -24000.0 363150.0 -32850.0 364050.0 ; - RECT -32850.0 355050.0 -39600.0 355950.0 ; - RECT -23400.0 357300.0 -24600.0 358500.0 ; - RECT -23550.0 389700.0 -24450.0 393450.0 ; - RECT -18750.0 354300.0 -16800.0 353100.0 ; - RECT -30600.0 354300.0 -28650.0 353100.0 ; - RECT -29250.0 359100.0 -31050.0 357900.0 ; - RECT -19950.0 359100.0 -16350.0 357900.0 ; - RECT -29250.0 356400.0 -19950.0 355500.0 ; - RECT -19950.0 359100.0 -18750.0 357900.0 ; - RECT -19950.0 356700.0 -18750.0 355500.0 ; - RECT -19950.0 356700.0 -18750.0 355500.0 ; - RECT -19950.0 359100.0 -18750.0 357900.0 ; - RECT -29250.0 359100.0 -28050.0 357900.0 ; - RECT -29250.0 356700.0 -28050.0 355500.0 ; - RECT -29250.0 356700.0 -28050.0 355500.0 ; - RECT -29250.0 359100.0 -28050.0 357900.0 ; - RECT -19350.0 354300.0 -18150.0 353100.0 ; - RECT -29250.0 354300.0 -28050.0 353100.0 ; - RECT -24600.0 358500.0 -23400.0 357300.0 ; - RECT -24600.0 358500.0 -23400.0 357300.0 ; - RECT -24450.0 355950.0 -23550.0 355050.0 ; - RECT -17250.0 360900.0 -16350.0 351300.0 ; - RECT -31050.0 360900.0 -30150.0 351300.0 ; - RECT -20250.0 375300.0 -19050.0 376500.0 ; - RECT -20250.0 377700.0 -19050.0 378900.0 ; - RECT -20250.0 377700.0 -19050.0 378900.0 ; - RECT -20250.0 375300.0 -19050.0 376500.0 ; - RECT -31050.0 458250.0 -30150.0 459150.0 ; - RECT -3450.0 458250.0 -2550.0 459150.0 ; - RECT -31050.0 456900.0 -30150.0 458700.0 ; - RECT -30600.0 458250.0 -3000.0 459150.0 ; - RECT -3450.0 456900.0 -2550.0 458700.0 ; - RECT -14850.0 396300.0 -16800.0 397500.0 ; - RECT -3000.0 396300.0 -4950.0 397500.0 ; - RECT -4350.0 391500.0 -2550.0 392700.0 ; - RECT -13650.0 391500.0 -17250.0 392700.0 ; - RECT -4350.0 394200.0 -13650.0 395100.0 ; - RECT -13650.0 391500.0 -14850.0 392700.0 ; - RECT -13650.0 393900.0 -14850.0 395100.0 ; - RECT -13650.0 393900.0 -14850.0 395100.0 ; - RECT -13650.0 391500.0 -14850.0 392700.0 ; - RECT -4350.0 391500.0 -5550.0 392700.0 ; - RECT -4350.0 393900.0 -5550.0 395100.0 ; - RECT -4350.0 393900.0 -5550.0 395100.0 ; - RECT -4350.0 391500.0 -5550.0 392700.0 ; - RECT -14250.0 396300.0 -15450.0 397500.0 ; - RECT -4350.0 396300.0 -5550.0 397500.0 ; - RECT -9000.0 392100.0 -10200.0 393300.0 ; - RECT -9000.0 392100.0 -10200.0 393300.0 ; - RECT -9150.0 394650.0 -10050.0 395550.0 ; - RECT -16350.0 389700.0 -17250.0 399300.0 ; - RECT -2550.0 389700.0 -3450.0 399300.0 ; - RECT -14850.0 405900.0 -16800.0 407100.0 ; - RECT -3000.0 405900.0 -4950.0 407100.0 ; - RECT -4350.0 401100.0 -2550.0 402300.0 ; - RECT -13650.0 401100.0 -17250.0 402300.0 ; - RECT -4350.0 403800.0 -13650.0 404700.0 ; - RECT -13650.0 401100.0 -14850.0 402300.0 ; - RECT -13650.0 403500.0 -14850.0 404700.0 ; - RECT -13650.0 403500.0 -14850.0 404700.0 ; - RECT -13650.0 401100.0 -14850.0 402300.0 ; - RECT -4350.0 401100.0 -5550.0 402300.0 ; - RECT -4350.0 403500.0 -5550.0 404700.0 ; - RECT -4350.0 403500.0 -5550.0 404700.0 ; - RECT -4350.0 401100.0 -5550.0 402300.0 ; - RECT -14250.0 405900.0 -15450.0 407100.0 ; - RECT -4350.0 405900.0 -5550.0 407100.0 ; - RECT -9000.0 401700.0 -10200.0 402900.0 ; - RECT -9000.0 401700.0 -10200.0 402900.0 ; - RECT -9150.0 404250.0 -10050.0 405150.0 ; - RECT -16350.0 399300.0 -17250.0 408900.0 ; - RECT -2550.0 399300.0 -3450.0 408900.0 ; - RECT -10200.0 401700.0 -9000.0 402900.0 ; - RECT -14850.0 415500.0 -16800.0 416700.0 ; - RECT -3000.0 415500.0 -4950.0 416700.0 ; - RECT -4350.0 410700.0 -2550.0 411900.0 ; - RECT -13650.0 410700.0 -17250.0 411900.0 ; - RECT -4350.0 413400.0 -13650.0 414300.0 ; - RECT -13650.0 410700.0 -14850.0 411900.0 ; - RECT -13650.0 413100.0 -14850.0 414300.0 ; - RECT -13650.0 413100.0 -14850.0 414300.0 ; - RECT -13650.0 410700.0 -14850.0 411900.0 ; - RECT -4350.0 410700.0 -5550.0 411900.0 ; - RECT -4350.0 413100.0 -5550.0 414300.0 ; - RECT -4350.0 413100.0 -5550.0 414300.0 ; - RECT -4350.0 410700.0 -5550.0 411900.0 ; - RECT -14250.0 415500.0 -15450.0 416700.0 ; - RECT -4350.0 415500.0 -5550.0 416700.0 ; - RECT -9000.0 411300.0 -10200.0 412500.0 ; - RECT -9000.0 411300.0 -10200.0 412500.0 ; - RECT -9150.0 413850.0 -10050.0 414750.0 ; - RECT -16350.0 408900.0 -17250.0 418500.0 ; - RECT -2550.0 408900.0 -3450.0 418500.0 ; - RECT -10200.0 411300.0 -9000.0 412500.0 ; - RECT -14850.0 425100.0 -16800.0 426300.0 ; - RECT -3000.0 425100.0 -4950.0 426300.0 ; - RECT -4350.0 420300.0 -2550.0 421500.0 ; - RECT -13650.0 420300.0 -17250.0 421500.0 ; - RECT -4350.0 423000.0 -13650.0 423900.0 ; - RECT -13650.0 420300.0 -14850.0 421500.0 ; - RECT -13650.0 422700.0 -14850.0 423900.0 ; - RECT -13650.0 422700.0 -14850.0 423900.0 ; - RECT -13650.0 420300.0 -14850.0 421500.0 ; - RECT -4350.0 420300.0 -5550.0 421500.0 ; - RECT -4350.0 422700.0 -5550.0 423900.0 ; - RECT -4350.0 422700.0 -5550.0 423900.0 ; - RECT -4350.0 420300.0 -5550.0 421500.0 ; - RECT -14250.0 425100.0 -15450.0 426300.0 ; - RECT -4350.0 425100.0 -5550.0 426300.0 ; - RECT -9000.0 420900.0 -10200.0 422100.0 ; - RECT -9000.0 420900.0 -10200.0 422100.0 ; - RECT -9150.0 423450.0 -10050.0 424350.0 ; - RECT -16350.0 418500.0 -17250.0 428100.0 ; - RECT -2550.0 418500.0 -3450.0 428100.0 ; - RECT -10200.0 420900.0 -9000.0 422100.0 ; - RECT -14850.0 434700.0 -16800.0 435900.0 ; - RECT -3000.0 434700.0 -4950.0 435900.0 ; - RECT -4350.0 429900.0 -2550.0 431100.0 ; - RECT -13650.0 429900.0 -17250.0 431100.0 ; - RECT -4350.0 432600.0 -13650.0 433500.0 ; - RECT -13650.0 429900.0 -14850.0 431100.0 ; - RECT -13650.0 432300.0 -14850.0 433500.0 ; - RECT -13650.0 432300.0 -14850.0 433500.0 ; - RECT -13650.0 429900.0 -14850.0 431100.0 ; - RECT -4350.0 429900.0 -5550.0 431100.0 ; - RECT -4350.0 432300.0 -5550.0 433500.0 ; - RECT -4350.0 432300.0 -5550.0 433500.0 ; - RECT -4350.0 429900.0 -5550.0 431100.0 ; - RECT -14250.0 434700.0 -15450.0 435900.0 ; - RECT -4350.0 434700.0 -5550.0 435900.0 ; - RECT -9000.0 430500.0 -10200.0 431700.0 ; - RECT -9000.0 430500.0 -10200.0 431700.0 ; - RECT -9150.0 433050.0 -10050.0 433950.0 ; - RECT -16350.0 428100.0 -17250.0 437700.0 ; - RECT -2550.0 428100.0 -3450.0 437700.0 ; - RECT -10200.0 430500.0 -9000.0 431700.0 ; - RECT -14850.0 444300.0 -16800.0 445500.0 ; - RECT -3000.0 444300.0 -4950.0 445500.0 ; - RECT -4350.0 439500.0 -2550.0 440700.0 ; - RECT -13650.0 439500.0 -17250.0 440700.0 ; - RECT -4350.0 442200.0 -13650.0 443100.0 ; - RECT -13650.0 439500.0 -14850.0 440700.0 ; - RECT -13650.0 441900.0 -14850.0 443100.0 ; - RECT -13650.0 441900.0 -14850.0 443100.0 ; - RECT -13650.0 439500.0 -14850.0 440700.0 ; - RECT -4350.0 439500.0 -5550.0 440700.0 ; - RECT -4350.0 441900.0 -5550.0 443100.0 ; - RECT -4350.0 441900.0 -5550.0 443100.0 ; - RECT -4350.0 439500.0 -5550.0 440700.0 ; - RECT -14250.0 444300.0 -15450.0 445500.0 ; - RECT -4350.0 444300.0 -5550.0 445500.0 ; - RECT -9000.0 440100.0 -10200.0 441300.0 ; - RECT -9000.0 440100.0 -10200.0 441300.0 ; - RECT -9150.0 442650.0 -10050.0 443550.0 ; - RECT -16350.0 437700.0 -17250.0 447300.0 ; - RECT -2550.0 437700.0 -3450.0 447300.0 ; - RECT -10200.0 440100.0 -9000.0 441300.0 ; - RECT -14850.0 453900.0 -16800.0 455100.0 ; - RECT -3000.0 453900.0 -4950.0 455100.0 ; - RECT -4350.0 449100.0 -2550.0 450300.0 ; - RECT -13650.0 449100.0 -17250.0 450300.0 ; - RECT -4350.0 451800.0 -13650.0 452700.0 ; - RECT -13650.0 449100.0 -14850.0 450300.0 ; - RECT -13650.0 451500.0 -14850.0 452700.0 ; - RECT -13650.0 451500.0 -14850.0 452700.0 ; - RECT -13650.0 449100.0 -14850.0 450300.0 ; - RECT -4350.0 449100.0 -5550.0 450300.0 ; - RECT -4350.0 451500.0 -5550.0 452700.0 ; - RECT -4350.0 451500.0 -5550.0 452700.0 ; - RECT -4350.0 449100.0 -5550.0 450300.0 ; - RECT -14250.0 453900.0 -15450.0 455100.0 ; - RECT -4350.0 453900.0 -5550.0 455100.0 ; - RECT -9000.0 449700.0 -10200.0 450900.0 ; - RECT -9000.0 449700.0 -10200.0 450900.0 ; - RECT -9150.0 452250.0 -10050.0 453150.0 ; - RECT -16350.0 447300.0 -17250.0 456900.0 ; - RECT -2550.0 447300.0 -3450.0 456900.0 ; - RECT -10200.0 449700.0 -9000.0 450900.0 ; - RECT -18750.0 440700.0 -16800.0 439500.0 ; - RECT -30600.0 440700.0 -28650.0 439500.0 ; - RECT -29250.0 445500.0 -31050.0 444300.0 ; - RECT -19950.0 445500.0 -16350.0 444300.0 ; - RECT -29250.0 442800.0 -19950.0 441900.0 ; - RECT -19950.0 445500.0 -18750.0 444300.0 ; - RECT -19950.0 443100.0 -18750.0 441900.0 ; - RECT -19950.0 443100.0 -18750.0 441900.0 ; - RECT -19950.0 445500.0 -18750.0 444300.0 ; - RECT -29250.0 445500.0 -28050.0 444300.0 ; - RECT -29250.0 443100.0 -28050.0 441900.0 ; - RECT -29250.0 443100.0 -28050.0 441900.0 ; - RECT -29250.0 445500.0 -28050.0 444300.0 ; - RECT -19350.0 440700.0 -18150.0 439500.0 ; - RECT -29250.0 440700.0 -28050.0 439500.0 ; - RECT -24600.0 444900.0 -23400.0 443700.0 ; - RECT -24600.0 444900.0 -23400.0 443700.0 ; - RECT -24450.0 442350.0 -23550.0 441450.0 ; - RECT -17250.0 447300.0 -16350.0 437700.0 ; - RECT -31050.0 447300.0 -30150.0 437700.0 ; - RECT -24600.0 443700.0 -23400.0 444900.0 ; - RECT -18750.0 431100.0 -16800.0 429900.0 ; - RECT -30600.0 431100.0 -28650.0 429900.0 ; - RECT -29250.0 435900.0 -31050.0 434700.0 ; - RECT -19950.0 435900.0 -16350.0 434700.0 ; - RECT -29250.0 433200.0 -19950.0 432300.0 ; - RECT -19950.0 435900.0 -18750.0 434700.0 ; - RECT -19950.0 433500.0 -18750.0 432300.0 ; - RECT -19950.0 433500.0 -18750.0 432300.0 ; - RECT -19950.0 435900.0 -18750.0 434700.0 ; - RECT -29250.0 435900.0 -28050.0 434700.0 ; - RECT -29250.0 433500.0 -28050.0 432300.0 ; - RECT -29250.0 433500.0 -28050.0 432300.0 ; - RECT -29250.0 435900.0 -28050.0 434700.0 ; - RECT -19350.0 431100.0 -18150.0 429900.0 ; - RECT -29250.0 431100.0 -28050.0 429900.0 ; - RECT -24600.0 435300.0 -23400.0 434100.0 ; - RECT -24600.0 435300.0 -23400.0 434100.0 ; - RECT -24450.0 432750.0 -23550.0 431850.0 ; - RECT -17250.0 437700.0 -16350.0 428100.0 ; - RECT -31050.0 437700.0 -30150.0 428100.0 ; - RECT -24600.0 434100.0 -23400.0 435300.0 ; - RECT -18750.0 421500.0 -16800.0 420300.0 ; - RECT -30600.0 421500.0 -28650.0 420300.0 ; - RECT -29250.0 426300.0 -31050.0 425100.0 ; - RECT -19950.0 426300.0 -16350.0 425100.0 ; - RECT -29250.0 423600.0 -19950.0 422700.0 ; - RECT -19950.0 426300.0 -18750.0 425100.0 ; - RECT -19950.0 423900.0 -18750.0 422700.0 ; - RECT -19950.0 423900.0 -18750.0 422700.0 ; - RECT -19950.0 426300.0 -18750.0 425100.0 ; - RECT -29250.0 426300.0 -28050.0 425100.0 ; - RECT -29250.0 423900.0 -28050.0 422700.0 ; - RECT -29250.0 423900.0 -28050.0 422700.0 ; - RECT -29250.0 426300.0 -28050.0 425100.0 ; - RECT -19350.0 421500.0 -18150.0 420300.0 ; - RECT -29250.0 421500.0 -28050.0 420300.0 ; - RECT -24600.0 425700.0 -23400.0 424500.0 ; - RECT -24600.0 425700.0 -23400.0 424500.0 ; - RECT -24450.0 423150.0 -23550.0 422250.0 ; - RECT -17250.0 428100.0 -16350.0 418500.0 ; - RECT -31050.0 428100.0 -30150.0 418500.0 ; - RECT -24600.0 424500.0 -23400.0 425700.0 ; - RECT -18750.0 411900.0 -16800.0 410700.0 ; - RECT -30600.0 411900.0 -28650.0 410700.0 ; - RECT -29250.0 416700.0 -31050.0 415500.0 ; - RECT -19950.0 416700.0 -16350.0 415500.0 ; - RECT -29250.0 414000.0 -19950.0 413100.0 ; - RECT -19950.0 416700.0 -18750.0 415500.0 ; - RECT -19950.0 414300.0 -18750.0 413100.0 ; - RECT -19950.0 414300.0 -18750.0 413100.0 ; - RECT -19950.0 416700.0 -18750.0 415500.0 ; - RECT -29250.0 416700.0 -28050.0 415500.0 ; - RECT -29250.0 414300.0 -28050.0 413100.0 ; - RECT -29250.0 414300.0 -28050.0 413100.0 ; - RECT -29250.0 416700.0 -28050.0 415500.0 ; - RECT -19350.0 411900.0 -18150.0 410700.0 ; - RECT -29250.0 411900.0 -28050.0 410700.0 ; - RECT -24600.0 416100.0 -23400.0 414900.0 ; - RECT -24600.0 416100.0 -23400.0 414900.0 ; - RECT -24450.0 413550.0 -23550.0 412650.0 ; - RECT -17250.0 418500.0 -16350.0 408900.0 ; - RECT -31050.0 418500.0 -30150.0 408900.0 ; - RECT -24600.0 414900.0 -23400.0 416100.0 ; - RECT -18750.0 402300.0 -16800.0 401100.0 ; - RECT -30600.0 402300.0 -28650.0 401100.0 ; - RECT -29250.0 407100.0 -31050.0 405900.0 ; - RECT -19950.0 407100.0 -16350.0 405900.0 ; - RECT -29250.0 404400.0 -19950.0 403500.0 ; - RECT -19950.0 407100.0 -18750.0 405900.0 ; - RECT -19950.0 404700.0 -18750.0 403500.0 ; - RECT -19950.0 404700.0 -18750.0 403500.0 ; - RECT -19950.0 407100.0 -18750.0 405900.0 ; - RECT -29250.0 407100.0 -28050.0 405900.0 ; - RECT -29250.0 404700.0 -28050.0 403500.0 ; - RECT -29250.0 404700.0 -28050.0 403500.0 ; - RECT -29250.0 407100.0 -28050.0 405900.0 ; - RECT -19350.0 402300.0 -18150.0 401100.0 ; - RECT -29250.0 402300.0 -28050.0 401100.0 ; - RECT -24600.0 406500.0 -23400.0 405300.0 ; - RECT -24600.0 406500.0 -23400.0 405300.0 ; - RECT -24450.0 403950.0 -23550.0 403050.0 ; - RECT -17250.0 408900.0 -16350.0 399300.0 ; - RECT -31050.0 408900.0 -30150.0 399300.0 ; - RECT -24600.0 405300.0 -23400.0 406500.0 ; - RECT -18750.0 392700.0 -16800.0 391500.0 ; - RECT -30600.0 392700.0 -28650.0 391500.0 ; - RECT -29250.0 397500.0 -31050.0 396300.0 ; - RECT -19950.0 397500.0 -16350.0 396300.0 ; - RECT -29250.0 394800.0 -19950.0 393900.0 ; - RECT -19950.0 397500.0 -18750.0 396300.0 ; - RECT -19950.0 395100.0 -18750.0 393900.0 ; - RECT -19950.0 395100.0 -18750.0 393900.0 ; - RECT -19950.0 397500.0 -18750.0 396300.0 ; - RECT -29250.0 397500.0 -28050.0 396300.0 ; - RECT -29250.0 395100.0 -28050.0 393900.0 ; - RECT -29250.0 395100.0 -28050.0 393900.0 ; - RECT -29250.0 397500.0 -28050.0 396300.0 ; - RECT -19350.0 392700.0 -18150.0 391500.0 ; - RECT -29250.0 392700.0 -28050.0 391500.0 ; - RECT -24600.0 396900.0 -23400.0 395700.0 ; - RECT -24600.0 396900.0 -23400.0 395700.0 ; - RECT -24450.0 394350.0 -23550.0 393450.0 ; - RECT -17250.0 399300.0 -16350.0 389700.0 ; - RECT -31050.0 399300.0 -30150.0 389700.0 ; - RECT -24600.0 395700.0 -23400.0 396900.0 ; - RECT -10200.0 394500.0 -9000.0 395700.0 ; - RECT -10200.0 423300.0 -9000.0 424500.0 ; - RECT -10200.0 452100.0 -9000.0 453300.0 ; - RECT -24600.0 422100.0 -23400.0 423300.0 ; - RECT -10200.0 392100.0 -9000.0 393300.0 ; - RECT -24450.0 389700.0 -23550.0 393450.0 ; - RECT -17250.0 389700.0 -16350.0 456900.0 ; - RECT -31050.0 389700.0 -30150.0 456900.0 ; - RECT -3450.0 389700.0 -2550.0 456900.0 ; - RECT -36000.0 374700.0 -46200.0 360900.0 ; - RECT -36000.0 374700.0 -46200.0 388500.0 ; - RECT -36000.0 402300.0 -46200.0 388500.0 ; - RECT -36000.0 402300.0 -46200.0 416100.0 ; - RECT -36000.0 429900.0 -46200.0 416100.0 ; - RECT -35400.0 376200.0 -46800.0 377400.0 ; - RECT -35400.0 399600.0 -46800.0 400800.0 ; - RECT -35400.0 403800.0 -46800.0 405000.0 ; - RECT -35400.0 427200.0 -46800.0 428400.0 ; - RECT -35400.0 387900.0 -46800.0 388800.0 ; - RECT -35400.0 415500.0 -46800.0 416400.0 ; - RECT -30450.0 376200.0 -31650.0 377400.0 ; - RECT -30450.0 399600.0 -31650.0 400800.0 ; - RECT -30450.0 403800.0 -31650.0 405000.0 ; - RECT -30450.0 427200.0 -31650.0 428400.0 ; - RECT -30600.0 389700.0 -31800.0 390900.0 ; - RECT -30000.0 350100.0 -31200.0 351300.0 ; - RECT -36600.0 350700.0 -35400.0 351900.0 ; - RECT -46800.0 350700.0 -45600.0 351900.0 ; - RECT -23400.0 376500.0 -24600.0 377700.0 ; - RECT -33450.0 363000.0 -32250.0 364200.0 ; - RECT -33450.0 354900.0 -32250.0 356100.0 ; - RECT -40200.0 354900.0 -39000.0 356100.0 ; - RECT -9000.0 341700.0 -9900.0 392100.0 ; - RECT -23550.0 341700.0 -24450.0 355050.0 ; - RECT -48300.0 341700.0 -49200.0 432150.0 ; - RECT -16350.0 341700.0 -17250.0 389700.0 ; - RECT -30150.0 341700.0 -31050.0 351300.0 ; - RECT -2550.0 341700.0 -3450.0 389700.0 ; - RECT -8850.0 265050.0 -10050.0 263850.0 ; - RECT -8850.0 224100.0 -10050.0 222900.0 ; - RECT -18900.0 185250.0 -20100.0 184050.0 ; - RECT -22950.0 265050.0 -24150.0 263850.0 ; - RECT -25650.0 270450.0 -26850.0 269250.0 ; - RECT -22200.0 307800.0 -23400.0 306600.0 ; - RECT -24900.0 310800.0 -26100.0 309600.0 ; - RECT -10950.0 283950.0 -12150.0 282750.0 ; - RECT -9000.0 281250.0 -10200.0 280050.0 ; - RECT -7050.0 273150.0 -8250.0 271950.0 ; - RECT -38550.0 283950.0 -39750.0 282750.0 ; - RECT -36600.0 273150.0 -37800.0 271950.0 ; - RECT -34650.0 275850.0 -35850.0 274650.0 ; - RECT -22950.0 302100.0 -24150.0 303300.0 ; - RECT -22200.0 319200.0 -23400.0 320400.0 ; - RECT -36600.0 341700.0 -37800.0 342900.0 ; - RECT -23400.0 321900.0 -24600.0 323100.0 ; - RECT -2400.0 267750.0 -3600.0 266550.0 ; - RECT -16200.0 278550.0 -17400.0 277350.0 ; - RECT -30000.0 267750.0 -31200.0 266550.0 ; - RECT -43800.0 278550.0 -45000.0 277350.0 ; - RECT -9000.0 181800.0 -10200.0 185400.0 ; - RECT -16350.0 181800.0 -17250.0 182700.0 ; - RECT -2550.0 181800.0 -3450.0 182700.0 ; - LAYER metal2 ; - RECT 109650.0 319800.0 110550.0 322500.0 ; - RECT 106950.0 339600.0 107850.0 342300.0 ; - RECT 101550.0 300000.0 102450.0 302700.0 ; - RECT 98850.0 317100.0 99750.0 319800.0 ; - RECT 104250.0 280650.0 105150.0 283350.0 ; - RECT 96150.0 261750.0 97050.0 264450.0 ; - RECT 6300.0 275250.0 7200.0 277950.0 ; - RECT -3000.0 266700.0 1800.0 267600.0 ; - RECT 96150.0 0.0 97050.0 461850.0 ; - RECT 98850.0 0.0 99750.0 461850.0 ; - RECT 101550.0 0.0 102450.0 461850.0 ; - RECT 104250.0 0.0 105150.0 461850.0 ; - RECT 106950.0 0.0 107850.0 461850.0 ; - RECT 109650.0 0.0 110550.0 461850.0 ; - RECT 85350.0 47400.0 86250.0 209400.0 ; - RECT 88050.0 47400.0 88950.0 209400.0 ; - RECT 90750.0 47400.0 91650.0 209400.0 ; - RECT 93450.0 47400.0 94350.0 209400.0 ; - RECT 122550.0 432600.0 123450.0 433800.0 ; - RECT 132750.0 432600.0 133650.0 433800.0 ; - RECT 121050.0 15750.0 121950.0 16650.0 ; - RECT 117900.0 15750.0 121500.0 16650.0 ; - RECT 121050.0 16200.0 121950.0 18000.0 ; - RECT 131250.0 15750.0 132150.0 16650.0 ; - RECT 128100.0 15750.0 131700.0 16650.0 ; - RECT 131250.0 16200.0 132150.0 18000.0 ; - RECT 53400.0 430200.0 54300.0 432300.0 ; - RECT 116400.0 209400.0 126600.0 223200.0 ; - RECT 116400.0 237000.0 126600.0 223200.0 ; - RECT 116400.0 237000.0 126600.0 250800.0 ; - RECT 116400.0 264600.0 126600.0 250800.0 ; - RECT 116400.0 264600.0 126600.0 278400.0 ; - RECT 116400.0 292200.0 126600.0 278400.0 ; - RECT 116400.0 292200.0 126600.0 306000.0 ; - RECT 116400.0 319800.0 126600.0 306000.0 ; - RECT 116400.0 319800.0 126600.0 333600.0 ; - RECT 116400.0 347400.0 126600.0 333600.0 ; - RECT 116400.0 347400.0 126600.0 361200.0 ; - RECT 116400.0 375000.0 126600.0 361200.0 ; - RECT 116400.0 375000.0 126600.0 388800.0 ; - RECT 116400.0 402600.0 126600.0 388800.0 ; - RECT 116400.0 402600.0 126600.0 416400.0 ; - RECT 116400.0 430200.0 126600.0 416400.0 ; - RECT 126600.0 209400.0 136800.0 223200.0 ; - RECT 126600.0 237000.0 136800.0 223200.0 ; - RECT 126600.0 237000.0 136800.0 250800.0 ; - RECT 126600.0 264600.0 136800.0 250800.0 ; - RECT 126600.0 264600.0 136800.0 278400.0 ; - RECT 126600.0 292200.0 136800.0 278400.0 ; - RECT 126600.0 292200.0 136800.0 306000.0 ; - RECT 126600.0 319800.0 136800.0 306000.0 ; - RECT 126600.0 319800.0 136800.0 333600.0 ; - RECT 126600.0 347400.0 136800.0 333600.0 ; - RECT 126600.0 347400.0 136800.0 361200.0 ; - RECT 126600.0 375000.0 136800.0 361200.0 ; - RECT 126600.0 375000.0 136800.0 388800.0 ; - RECT 126600.0 402600.0 136800.0 388800.0 ; - RECT 126600.0 402600.0 136800.0 416400.0 ; - RECT 126600.0 430200.0 136800.0 416400.0 ; - RECT 119400.0 210000.0 120600.0 433800.0 ; - RECT 122400.0 208800.0 123600.0 432600.0 ; - RECT 129600.0 210000.0 130800.0 433800.0 ; - RECT 132600.0 208800.0 133800.0 432600.0 ; - RECT 115800.0 208800.0 117000.0 432600.0 ; - RECT 126000.0 208800.0 127200.0 432600.0 ; - RECT 136200.0 208800.0 137400.0 432600.0 ; - RECT 119400.0 436200.0 120600.0 437400.0 ; - RECT 121800.0 436200.0 123450.0 437400.0 ; - RECT 119400.0 443400.0 120600.0 444600.0 ; - RECT 122550.0 443400.0 125400.0 444600.0 ; - RECT 119400.0 436200.0 120600.0 437400.0 ; - RECT 121800.0 436200.0 123000.0 437400.0 ; - RECT 119400.0 443400.0 120600.0 444600.0 ; - RECT 124200.0 443400.0 125400.0 444600.0 ; - RECT 119550.0 433800.0 120450.0 450600.0 ; - RECT 122550.0 433800.0 123450.0 450600.0 ; - RECT 129600.0 436200.0 130800.0 437400.0 ; - RECT 132000.0 436200.0 133650.0 437400.0 ; - RECT 129600.0 443400.0 130800.0 444600.0 ; - RECT 132750.0 443400.0 135600.0 444600.0 ; - RECT 129600.0 436200.0 130800.0 437400.0 ; - RECT 132000.0 436200.0 133200.0 437400.0 ; - RECT 129600.0 443400.0 130800.0 444600.0 ; - RECT 134400.0 443400.0 135600.0 444600.0 ; - RECT 129750.0 433800.0 130650.0 450600.0 ; - RECT 132750.0 433800.0 133650.0 450600.0 ; - RECT 119550.0 433800.0 120450.0 450600.0 ; - RECT 122550.0 433800.0 123450.0 450600.0 ; - RECT 129750.0 433800.0 130650.0 450600.0 ; - RECT 132750.0 433800.0 133650.0 450600.0 ; - RECT 116400.0 160500.0 126600.0 209400.0 ; - RECT 126600.0 160500.0 136800.0 209400.0 ; - RECT 119400.0 160500.0 120600.0 173700.0 ; - RECT 122400.0 160500.0 123600.0 173700.0 ; - RECT 129600.0 160500.0 130800.0 173700.0 ; - RECT 132600.0 160500.0 133800.0 173700.0 ; - RECT 116400.0 99900.0 126600.0 160500.0 ; - RECT 126600.0 99900.0 136800.0 160500.0 ; - RECT 120900.0 99900.0 122100.0 102900.0 ; - RECT 131100.0 99900.0 132300.0 102900.0 ; - RECT 119400.0 158400.0 120600.0 160500.0 ; - RECT 122400.0 153000.0 123600.0 160500.0 ; - RECT 129600.0 158400.0 130800.0 160500.0 ; - RECT 132600.0 153000.0 133800.0 160500.0 ; - RECT 116400.0 39900.0 126600.0 99900.0 ; - RECT 136800.0 39900.0 126600.0 99900.0 ; - RECT 120900.0 97500.0 123600.0 98700.0 ; - RECT 118200.0 95400.0 119400.0 99900.0 ; - RECT 129600.0 97500.0 132300.0 98700.0 ; - RECT 133800.0 95400.0 135000.0 99900.0 ; - RECT 126000.0 39900.0 127200.0 99900.0 ; - RECT 116400.0 39900.0 126600.0 18000.0 ; - RECT 126600.0 39900.0 136800.0 18000.0 ; - RECT 120900.0 24900.0 122100.0 18000.0 ; - RECT 131100.0 24900.0 132300.0 18000.0 ; - RECT 120900.0 39900.0 122100.0 38400.0 ; - RECT 131100.0 39900.0 132300.0 38400.0 ; - RECT 9900.0 99000.0 10800.0 430200.0 ; - RECT 12000.0 99000.0 12900.0 430200.0 ; - RECT 14100.0 99000.0 15000.0 430200.0 ; - RECT 16200.0 99000.0 17100.0 430200.0 ; - RECT 18300.0 99000.0 19200.0 430200.0 ; - RECT 20400.0 99000.0 21300.0 430200.0 ; - RECT 22500.0 99000.0 23400.0 430200.0 ; - RECT 24600.0 99000.0 25500.0 430200.0 ; - RECT 56700.0 99000.0 55800.0 152400.0 ; - RECT 53700.0 99000.0 52800.0 152400.0 ; - RECT 62700.0 99000.0 61800.0 152400.0 ; - RECT 59700.0 99000.0 58800.0 152400.0 ; - RECT 46350.0 106350.0 45450.0 107250.0 ; - RECT 43950.0 106350.0 43050.0 107250.0 ; - RECT 46350.0 106800.0 45450.0 109650.0 ; - RECT 45900.0 106350.0 43500.0 107250.0 ; - RECT 43950.0 102150.0 43050.0 106800.0 ; - RECT 46500.0 109650.0 45300.0 110850.0 ; - RECT 44100.0 100950.0 42900.0 102150.0 ; - RECT 42900.0 106200.0 44100.0 107400.0 ; - RECT 46350.0 119250.0 45450.0 118350.0 ; - RECT 43950.0 119250.0 43050.0 118350.0 ; - RECT 46350.0 118800.0 45450.0 115950.0 ; - RECT 45900.0 119250.0 43500.0 118350.0 ; - RECT 43950.0 123450.0 43050.0 118800.0 ; - RECT 46500.0 115950.0 45300.0 114750.0 ; - RECT 44100.0 124650.0 42900.0 123450.0 ; - RECT 42900.0 119400.0 44100.0 118200.0 ; - RECT 46350.0 133950.0 45450.0 134850.0 ; - RECT 43950.0 133950.0 43050.0 134850.0 ; - RECT 46350.0 134400.0 45450.0 137250.0 ; - RECT 45900.0 133950.0 43500.0 134850.0 ; - RECT 43950.0 129750.0 43050.0 134400.0 ; - RECT 46500.0 137250.0 45300.0 138450.0 ; - RECT 44100.0 128550.0 42900.0 129750.0 ; - RECT 42900.0 133800.0 44100.0 135000.0 ; - RECT 46350.0 146850.0 45450.0 145950.0 ; - RECT 43950.0 146850.0 43050.0 145950.0 ; - RECT 46350.0 146400.0 45450.0 143550.0 ; - RECT 45900.0 146850.0 43500.0 145950.0 ; - RECT 43950.0 151050.0 43050.0 146400.0 ; - RECT 46500.0 143550.0 45300.0 142350.0 ; - RECT 44100.0 152250.0 42900.0 151050.0 ; - RECT 42900.0 147000.0 44100.0 145800.0 ; - RECT 61650.0 109500.0 62850.0 110700.0 ; - RECT 80250.0 105000.0 81450.0 106200.0 ; - RECT 58650.0 123300.0 59850.0 124500.0 ; - RECT 77250.0 119400.0 78450.0 120600.0 ; - RECT 80250.0 128100.0 81450.0 129300.0 ; - RECT 55650.0 128100.0 56850.0 129300.0 ; - RECT 77250.0 141900.0 78450.0 143100.0 ; - RECT 52650.0 141900.0 53850.0 143100.0 ; - RECT 61650.0 106200.0 62850.0 107400.0 ; - RECT 58650.0 103500.0 59850.0 104700.0 ; - RECT 55650.0 118200.0 56850.0 119400.0 ; - RECT 58650.0 120900.0 59850.0 122100.0 ; - RECT 61650.0 133800.0 62850.0 135000.0 ; - RECT 52650.0 131100.0 53850.0 132300.0 ; - RECT 55650.0 145800.0 56850.0 147000.0 ; - RECT 52650.0 148500.0 53850.0 149700.0 ; - RECT 81300.0 99000.0 80400.0 152400.0 ; - RECT 78300.0 99000.0 77400.0 152400.0 ; - RECT 56700.0 154200.0 55800.0 207600.0 ; - RECT 53700.0 154200.0 52800.0 207600.0 ; - RECT 62700.0 154200.0 61800.0 207600.0 ; - RECT 59700.0 154200.0 58800.0 207600.0 ; - RECT 46350.0 161550.0 45450.0 162450.0 ; - RECT 43950.0 161550.0 43050.0 162450.0 ; - RECT 46350.0 162000.0 45450.0 164850.0 ; - RECT 45900.0 161550.0 43500.0 162450.0 ; - RECT 43950.0 157350.0 43050.0 162000.0 ; - RECT 46500.0 164850.0 45300.0 166050.0 ; - RECT 44100.0 156150.0 42900.0 157350.0 ; - RECT 42900.0 161400.0 44100.0 162600.0 ; - RECT 46350.0 174450.0 45450.0 173550.0 ; - RECT 43950.0 174450.0 43050.0 173550.0 ; - RECT 46350.0 174000.0 45450.0 171150.0 ; - RECT 45900.0 174450.0 43500.0 173550.0 ; - RECT 43950.0 178650.0 43050.0 174000.0 ; - RECT 46500.0 171150.0 45300.0 169950.0 ; - RECT 44100.0 179850.0 42900.0 178650.0 ; - RECT 42900.0 174600.0 44100.0 173400.0 ; - RECT 46350.0 189150.0 45450.0 190050.0 ; - RECT 43950.0 189150.0 43050.0 190050.0 ; - RECT 46350.0 189600.0 45450.0 192450.0 ; - RECT 45900.0 189150.0 43500.0 190050.0 ; - RECT 43950.0 184950.0 43050.0 189600.0 ; - RECT 46500.0 192450.0 45300.0 193650.0 ; - RECT 44100.0 183750.0 42900.0 184950.0 ; - RECT 42900.0 189000.0 44100.0 190200.0 ; - RECT 46350.0 202050.0 45450.0 201150.0 ; - RECT 43950.0 202050.0 43050.0 201150.0 ; - RECT 46350.0 201600.0 45450.0 198750.0 ; - RECT 45900.0 202050.0 43500.0 201150.0 ; - RECT 43950.0 206250.0 43050.0 201600.0 ; - RECT 46500.0 198750.0 45300.0 197550.0 ; - RECT 44100.0 207450.0 42900.0 206250.0 ; - RECT 42900.0 202200.0 44100.0 201000.0 ; - RECT 61650.0 164700.0 62850.0 165900.0 ; - RECT 80250.0 160200.0 81450.0 161400.0 ; - RECT 58650.0 178500.0 59850.0 179700.0 ; - RECT 77250.0 174600.0 78450.0 175800.0 ; - RECT 80250.0 183300.0 81450.0 184500.0 ; - RECT 55650.0 183300.0 56850.0 184500.0 ; - RECT 77250.0 197100.0 78450.0 198300.0 ; - RECT 52650.0 197100.0 53850.0 198300.0 ; - RECT 61650.0 161400.0 62850.0 162600.0 ; - RECT 58650.0 158700.0 59850.0 159900.0 ; - RECT 55650.0 173400.0 56850.0 174600.0 ; - RECT 58650.0 176100.0 59850.0 177300.0 ; - RECT 61650.0 189000.0 62850.0 190200.0 ; - RECT 52650.0 186300.0 53850.0 187500.0 ; - RECT 55650.0 201000.0 56850.0 202200.0 ; - RECT 52650.0 203700.0 53850.0 204900.0 ; - RECT 81300.0 154200.0 80400.0 207600.0 ; - RECT 78300.0 154200.0 77400.0 207600.0 ; - RECT 31050.0 216750.0 31950.0 217650.0 ; - RECT 33450.0 216750.0 34350.0 217650.0 ; - RECT 31050.0 217200.0 31950.0 220050.0 ; - RECT 31500.0 216750.0 33900.0 217650.0 ; - RECT 33450.0 212550.0 34350.0 217200.0 ; - RECT 30900.0 220050.0 32100.0 221250.0 ; - RECT 33300.0 211350.0 34500.0 212550.0 ; - RECT 34500.0 216600.0 33300.0 217800.0 ; - RECT 31050.0 229650.0 31950.0 228750.0 ; - RECT 33450.0 229650.0 34350.0 228750.0 ; - RECT 31050.0 229200.0 31950.0 226350.0 ; - RECT 31500.0 229650.0 33900.0 228750.0 ; - RECT 33450.0 233850.0 34350.0 229200.0 ; - RECT 30900.0 226350.0 32100.0 225150.0 ; - RECT 33300.0 235050.0 34500.0 233850.0 ; - RECT 34500.0 229800.0 33300.0 228600.0 ; - RECT 31050.0 244350.0 31950.0 245250.0 ; - RECT 33450.0 244350.0 34350.0 245250.0 ; - RECT 31050.0 244800.0 31950.0 247650.0 ; - RECT 31500.0 244350.0 33900.0 245250.0 ; - RECT 33450.0 240150.0 34350.0 244800.0 ; - RECT 30900.0 247650.0 32100.0 248850.0 ; - RECT 33300.0 238950.0 34500.0 240150.0 ; - RECT 34500.0 244200.0 33300.0 245400.0 ; - RECT 31050.0 257250.0 31950.0 256350.0 ; - RECT 33450.0 257250.0 34350.0 256350.0 ; - RECT 31050.0 256800.0 31950.0 253950.0 ; - RECT 31500.0 257250.0 33900.0 256350.0 ; - RECT 33450.0 261450.0 34350.0 256800.0 ; - RECT 30900.0 253950.0 32100.0 252750.0 ; - RECT 33300.0 262650.0 34500.0 261450.0 ; - RECT 34500.0 257400.0 33300.0 256200.0 ; - RECT 31050.0 271950.0 31950.0 272850.0 ; - RECT 33450.0 271950.0 34350.0 272850.0 ; - RECT 31050.0 272400.0 31950.0 275250.0 ; - RECT 31500.0 271950.0 33900.0 272850.0 ; - RECT 33450.0 267750.0 34350.0 272400.0 ; - RECT 30900.0 275250.0 32100.0 276450.0 ; - RECT 33300.0 266550.0 34500.0 267750.0 ; - RECT 34500.0 271800.0 33300.0 273000.0 ; - RECT 31050.0 284850.0 31950.0 283950.0 ; - RECT 33450.0 284850.0 34350.0 283950.0 ; - RECT 31050.0 284400.0 31950.0 281550.0 ; - RECT 31500.0 284850.0 33900.0 283950.0 ; - RECT 33450.0 289050.0 34350.0 284400.0 ; - RECT 30900.0 281550.0 32100.0 280350.0 ; - RECT 33300.0 290250.0 34500.0 289050.0 ; - RECT 34500.0 285000.0 33300.0 283800.0 ; - RECT 31050.0 299550.0 31950.0 300450.0 ; - RECT 33450.0 299550.0 34350.0 300450.0 ; - RECT 31050.0 300000.0 31950.0 302850.0 ; - RECT 31500.0 299550.0 33900.0 300450.0 ; - RECT 33450.0 295350.0 34350.0 300000.0 ; - RECT 30900.0 302850.0 32100.0 304050.0 ; - RECT 33300.0 294150.0 34500.0 295350.0 ; - RECT 34500.0 299400.0 33300.0 300600.0 ; - RECT 31050.0 312450.0 31950.0 311550.0 ; - RECT 33450.0 312450.0 34350.0 311550.0 ; - RECT 31050.0 312000.0 31950.0 309150.0 ; - RECT 31500.0 312450.0 33900.0 311550.0 ; - RECT 33450.0 316650.0 34350.0 312000.0 ; - RECT 30900.0 309150.0 32100.0 307950.0 ; - RECT 33300.0 317850.0 34500.0 316650.0 ; - RECT 34500.0 312600.0 33300.0 311400.0 ; - RECT 31050.0 327150.0 31950.0 328050.0 ; - RECT 33450.0 327150.0 34350.0 328050.0 ; - RECT 31050.0 327600.0 31950.0 330450.0 ; - RECT 31500.0 327150.0 33900.0 328050.0 ; - RECT 33450.0 322950.0 34350.0 327600.0 ; - RECT 30900.0 330450.0 32100.0 331650.0 ; - RECT 33300.0 321750.0 34500.0 322950.0 ; - RECT 34500.0 327000.0 33300.0 328200.0 ; - RECT 31050.0 340050.0 31950.0 339150.0 ; - RECT 33450.0 340050.0 34350.0 339150.0 ; - RECT 31050.0 339600.0 31950.0 336750.0 ; - RECT 31500.0 340050.0 33900.0 339150.0 ; - RECT 33450.0 344250.0 34350.0 339600.0 ; - RECT 30900.0 336750.0 32100.0 335550.0 ; - RECT 33300.0 345450.0 34500.0 344250.0 ; - RECT 34500.0 340200.0 33300.0 339000.0 ; - RECT 31050.0 354750.0 31950.0 355650.0 ; - RECT 33450.0 354750.0 34350.0 355650.0 ; - RECT 31050.0 355200.0 31950.0 358050.0 ; - RECT 31500.0 354750.0 33900.0 355650.0 ; - RECT 33450.0 350550.0 34350.0 355200.0 ; - RECT 30900.0 358050.0 32100.0 359250.0 ; - RECT 33300.0 349350.0 34500.0 350550.0 ; - RECT 34500.0 354600.0 33300.0 355800.0 ; - RECT 31050.0 367650.0 31950.0 366750.0 ; - RECT 33450.0 367650.0 34350.0 366750.0 ; - RECT 31050.0 367200.0 31950.0 364350.0 ; - RECT 31500.0 367650.0 33900.0 366750.0 ; - RECT 33450.0 371850.0 34350.0 367200.0 ; - RECT 30900.0 364350.0 32100.0 363150.0 ; - RECT 33300.0 373050.0 34500.0 371850.0 ; - RECT 34500.0 367800.0 33300.0 366600.0 ; - RECT 31050.0 382350.0 31950.0 383250.0 ; - RECT 33450.0 382350.0 34350.0 383250.0 ; - RECT 31050.0 382800.0 31950.0 385650.0 ; - RECT 31500.0 382350.0 33900.0 383250.0 ; - RECT 33450.0 378150.0 34350.0 382800.0 ; - RECT 30900.0 385650.0 32100.0 386850.0 ; - RECT 33300.0 376950.0 34500.0 378150.0 ; - RECT 34500.0 382200.0 33300.0 383400.0 ; - RECT 31050.0 395250.0 31950.0 394350.0 ; - RECT 33450.0 395250.0 34350.0 394350.0 ; - RECT 31050.0 394800.0 31950.0 391950.0 ; - RECT 31500.0 395250.0 33900.0 394350.0 ; - RECT 33450.0 399450.0 34350.0 394800.0 ; - RECT 30900.0 391950.0 32100.0 390750.0 ; - RECT 33300.0 400650.0 34500.0 399450.0 ; - RECT 34500.0 395400.0 33300.0 394200.0 ; - RECT 31050.0 409950.0 31950.0 410850.0 ; - RECT 33450.0 409950.0 34350.0 410850.0 ; - RECT 31050.0 410400.0 31950.0 413250.0 ; - RECT 31500.0 409950.0 33900.0 410850.0 ; - RECT 33450.0 405750.0 34350.0 410400.0 ; - RECT 30900.0 413250.0 32100.0 414450.0 ; - RECT 33300.0 404550.0 34500.0 405750.0 ; - RECT 34500.0 409800.0 33300.0 411000.0 ; - RECT 31050.0 422850.0 31950.0 421950.0 ; - RECT 33450.0 422850.0 34350.0 421950.0 ; - RECT 31050.0 422400.0 31950.0 419550.0 ; - RECT 31500.0 422850.0 33900.0 421950.0 ; - RECT 33450.0 427050.0 34350.0 422400.0 ; - RECT 30900.0 419550.0 32100.0 418350.0 ; - RECT 33300.0 428250.0 34500.0 427050.0 ; - RECT 34500.0 423000.0 33300.0 421800.0 ; - RECT 10950.0 105000.0 9750.0 106200.0 ; - RECT 13050.0 119400.0 11850.0 120600.0 ; - RECT 15150.0 132600.0 13950.0 133800.0 ; - RECT 17250.0 147000.0 16050.0 148200.0 ; - RECT 19350.0 160200.0 18150.0 161400.0 ; - RECT 21450.0 174600.0 20250.0 175800.0 ; - RECT 23550.0 187800.0 22350.0 189000.0 ; - RECT 25650.0 202200.0 24450.0 203400.0 ; - RECT 10950.0 216600.0 9750.0 217800.0 ; - RECT 19350.0 213900.0 18150.0 215100.0 ; - RECT 10950.0 228600.0 9750.0 229800.0 ; - RECT 21450.0 231300.0 20250.0 232500.0 ; - RECT 10950.0 244200.0 9750.0 245400.0 ; - RECT 23550.0 241500.0 22350.0 242700.0 ; - RECT 10950.0 256200.0 9750.0 257400.0 ; - RECT 25650.0 258900.0 24450.0 260100.0 ; - RECT 13050.0 271800.0 11850.0 273000.0 ; - RECT 19350.0 269100.0 18150.0 270300.0 ; - RECT 13050.0 283800.0 11850.0 285000.0 ; - RECT 21450.0 286500.0 20250.0 287700.0 ; - RECT 13050.0 299400.0 11850.0 300600.0 ; - RECT 23550.0 296700.0 22350.0 297900.0 ; - RECT 13050.0 311400.0 11850.0 312600.0 ; - RECT 25650.0 314100.0 24450.0 315300.0 ; - RECT 15150.0 327000.0 13950.0 328200.0 ; - RECT 19350.0 324300.0 18150.0 325500.0 ; - RECT 15150.0 339000.0 13950.0 340200.0 ; - RECT 21450.0 341700.0 20250.0 342900.0 ; - RECT 15150.0 354600.0 13950.0 355800.0 ; - RECT 23550.0 351900.0 22350.0 353100.0 ; - RECT 15150.0 366600.0 13950.0 367800.0 ; - RECT 25650.0 369300.0 24450.0 370500.0 ; - RECT 17250.0 382200.0 16050.0 383400.0 ; - RECT 19350.0 379500.0 18150.0 380700.0 ; - RECT 17250.0 394200.0 16050.0 395400.0 ; - RECT 21450.0 396900.0 20250.0 398100.0 ; - RECT 17250.0 409800.0 16050.0 411000.0 ; - RECT 23550.0 407100.0 22350.0 408300.0 ; - RECT 17250.0 421800.0 16050.0 423000.0 ; - RECT 25650.0 424500.0 24450.0 425700.0 ; - RECT 80400.0 99000.0 81300.0 152400.0 ; - RECT 77400.0 99000.0 78300.0 152400.0 ; - RECT 80400.0 154200.0 81300.0 207600.0 ; - RECT 77400.0 154200.0 78300.0 207600.0 ; - RECT 55350.0 214050.0 56250.0 214950.0 ; - RECT 55350.0 213600.0 56250.0 214500.0 ; - RECT 55800.0 214050.0 72000.0 214950.0 ; - RECT 55350.0 231450.0 56250.0 232350.0 ; - RECT 55350.0 231900.0 56250.0 232800.0 ; - RECT 55800.0 231450.0 72000.0 232350.0 ; - RECT 55350.0 241650.0 56250.0 242550.0 ; - RECT 55350.0 241200.0 56250.0 242100.0 ; - RECT 55800.0 241650.0 72000.0 242550.0 ; - RECT 55350.0 259050.0 56250.0 259950.0 ; - RECT 55350.0 259500.0 56250.0 260400.0 ; - RECT 55800.0 259050.0 72000.0 259950.0 ; - RECT 55350.0 269250.0 56250.0 270150.0 ; - RECT 55350.0 268800.0 56250.0 269700.0 ; - RECT 55800.0 269250.0 72000.0 270150.0 ; - RECT 55350.0 286650.0 56250.0 287550.0 ; - RECT 55350.0 287100.0 56250.0 288000.0 ; - RECT 55800.0 286650.0 72000.0 287550.0 ; - RECT 55350.0 296850.0 56250.0 297750.0 ; - RECT 55350.0 296400.0 56250.0 297300.0 ; - RECT 55800.0 296850.0 72000.0 297750.0 ; - RECT 55350.0 314250.0 56250.0 315150.0 ; - RECT 55350.0 314700.0 56250.0 315600.0 ; - RECT 55800.0 314250.0 72000.0 315150.0 ; - RECT 55350.0 324450.0 56250.0 325350.0 ; - RECT 55350.0 324000.0 56250.0 324900.0 ; - RECT 55800.0 324450.0 72000.0 325350.0 ; - RECT 55350.0 341850.0 56250.0 342750.0 ; - RECT 55350.0 342300.0 56250.0 343200.0 ; - RECT 55800.0 341850.0 72000.0 342750.0 ; - RECT 55350.0 352050.0 56250.0 352950.0 ; - RECT 55350.0 351600.0 56250.0 352500.0 ; - RECT 55800.0 352050.0 72000.0 352950.0 ; - RECT 55350.0 369450.0 56250.0 370350.0 ; - RECT 55350.0 369900.0 56250.0 370800.0 ; - RECT 55800.0 369450.0 72000.0 370350.0 ; - RECT 55350.0 379650.0 56250.0 380550.0 ; - RECT 55350.0 379200.0 56250.0 380100.0 ; - RECT 55800.0 379650.0 72000.0 380550.0 ; - RECT 55350.0 397050.0 56250.0 397950.0 ; - RECT 55350.0 397500.0 56250.0 398400.0 ; - RECT 55800.0 397050.0 72000.0 397950.0 ; - RECT 55350.0 407250.0 56250.0 408150.0 ; - RECT 55350.0 406800.0 56250.0 407700.0 ; - RECT 55800.0 407250.0 72000.0 408150.0 ; - RECT 55350.0 424650.0 56250.0 425550.0 ; - RECT 55350.0 425100.0 56250.0 426000.0 ; - RECT 55800.0 424650.0 72000.0 425550.0 ; - RECT 70950.0 216750.0 71850.0 217650.0 ; - RECT 73350.0 216750.0 74250.0 217650.0 ; - RECT 70950.0 217200.0 71850.0 220050.0 ; - RECT 71400.0 216750.0 73800.0 217650.0 ; - RECT 73350.0 212550.0 74250.0 217200.0 ; - RECT 70800.0 220050.0 72000.0 221250.0 ; - RECT 73200.0 211350.0 74400.0 212550.0 ; - RECT 74400.0 216600.0 73200.0 217800.0 ; - RECT 53250.0 215400.0 54450.0 216600.0 ; - RECT 55200.0 213000.0 56400.0 214200.0 ; - RECT 72000.0 213900.0 70800.0 215100.0 ; - RECT 70950.0 229650.0 71850.0 228750.0 ; - RECT 73350.0 229650.0 74250.0 228750.0 ; - RECT 70950.0 229200.0 71850.0 226350.0 ; - RECT 71400.0 229650.0 73800.0 228750.0 ; - RECT 73350.0 233850.0 74250.0 229200.0 ; - RECT 70800.0 226350.0 72000.0 225150.0 ; - RECT 73200.0 235050.0 74400.0 233850.0 ; - RECT 74400.0 229800.0 73200.0 228600.0 ; - RECT 53250.0 229800.0 54450.0 231000.0 ; - RECT 55200.0 232200.0 56400.0 233400.0 ; - RECT 72000.0 231300.0 70800.0 232500.0 ; - RECT 70950.0 244350.0 71850.0 245250.0 ; - RECT 73350.0 244350.0 74250.0 245250.0 ; - RECT 70950.0 244800.0 71850.0 247650.0 ; - RECT 71400.0 244350.0 73800.0 245250.0 ; - RECT 73350.0 240150.0 74250.0 244800.0 ; - RECT 70800.0 247650.0 72000.0 248850.0 ; - RECT 73200.0 238950.0 74400.0 240150.0 ; - RECT 74400.0 244200.0 73200.0 245400.0 ; - RECT 53250.0 243000.0 54450.0 244200.0 ; - RECT 55200.0 240600.0 56400.0 241800.0 ; - RECT 72000.0 241500.0 70800.0 242700.0 ; - RECT 70950.0 257250.0 71850.0 256350.0 ; - RECT 73350.0 257250.0 74250.0 256350.0 ; - RECT 70950.0 256800.0 71850.0 253950.0 ; - RECT 71400.0 257250.0 73800.0 256350.0 ; - RECT 73350.0 261450.0 74250.0 256800.0 ; - RECT 70800.0 253950.0 72000.0 252750.0 ; - RECT 73200.0 262650.0 74400.0 261450.0 ; - RECT 74400.0 257400.0 73200.0 256200.0 ; - RECT 53250.0 257400.0 54450.0 258600.0 ; - RECT 55200.0 259800.0 56400.0 261000.0 ; - RECT 72000.0 258900.0 70800.0 260100.0 ; - RECT 70950.0 271950.0 71850.0 272850.0 ; - RECT 73350.0 271950.0 74250.0 272850.0 ; - RECT 70950.0 272400.0 71850.0 275250.0 ; - RECT 71400.0 271950.0 73800.0 272850.0 ; - RECT 73350.0 267750.0 74250.0 272400.0 ; - RECT 70800.0 275250.0 72000.0 276450.0 ; - RECT 73200.0 266550.0 74400.0 267750.0 ; - RECT 74400.0 271800.0 73200.0 273000.0 ; - RECT 53250.0 270600.0 54450.0 271800.0 ; - RECT 55200.0 268200.0 56400.0 269400.0 ; - RECT 72000.0 269100.0 70800.0 270300.0 ; - RECT 70950.0 284850.0 71850.0 283950.0 ; - RECT 73350.0 284850.0 74250.0 283950.0 ; - RECT 70950.0 284400.0 71850.0 281550.0 ; - RECT 71400.0 284850.0 73800.0 283950.0 ; - RECT 73350.0 289050.0 74250.0 284400.0 ; - RECT 70800.0 281550.0 72000.0 280350.0 ; - RECT 73200.0 290250.0 74400.0 289050.0 ; - RECT 74400.0 285000.0 73200.0 283800.0 ; - RECT 53250.0 285000.0 54450.0 286200.0 ; - RECT 55200.0 287400.0 56400.0 288600.0 ; - RECT 72000.0 286500.0 70800.0 287700.0 ; - RECT 70950.0 299550.0 71850.0 300450.0 ; - RECT 73350.0 299550.0 74250.0 300450.0 ; - RECT 70950.0 300000.0 71850.0 302850.0 ; - RECT 71400.0 299550.0 73800.0 300450.0 ; - RECT 73350.0 295350.0 74250.0 300000.0 ; - RECT 70800.0 302850.0 72000.0 304050.0 ; - RECT 73200.0 294150.0 74400.0 295350.0 ; - RECT 74400.0 299400.0 73200.0 300600.0 ; - RECT 53250.0 298200.0 54450.0 299400.0 ; - RECT 55200.0 295800.0 56400.0 297000.0 ; - RECT 72000.0 296700.0 70800.0 297900.0 ; - RECT 70950.0 312450.0 71850.0 311550.0 ; - RECT 73350.0 312450.0 74250.0 311550.0 ; - RECT 70950.0 312000.0 71850.0 309150.0 ; - RECT 71400.0 312450.0 73800.0 311550.0 ; - RECT 73350.0 316650.0 74250.0 312000.0 ; - RECT 70800.0 309150.0 72000.0 307950.0 ; - RECT 73200.0 317850.0 74400.0 316650.0 ; - RECT 74400.0 312600.0 73200.0 311400.0 ; - RECT 53250.0 312600.0 54450.0 313800.0 ; - RECT 55200.0 315000.0 56400.0 316200.0 ; - RECT 72000.0 314100.0 70800.0 315300.0 ; - RECT 70950.0 327150.0 71850.0 328050.0 ; - RECT 73350.0 327150.0 74250.0 328050.0 ; - RECT 70950.0 327600.0 71850.0 330450.0 ; - RECT 71400.0 327150.0 73800.0 328050.0 ; - RECT 73350.0 322950.0 74250.0 327600.0 ; - RECT 70800.0 330450.0 72000.0 331650.0 ; - RECT 73200.0 321750.0 74400.0 322950.0 ; - RECT 74400.0 327000.0 73200.0 328200.0 ; - RECT 53250.0 325800.0 54450.0 327000.0 ; - RECT 55200.0 323400.0 56400.0 324600.0 ; - RECT 72000.0 324300.0 70800.0 325500.0 ; - RECT 70950.0 340050.0 71850.0 339150.0 ; - RECT 73350.0 340050.0 74250.0 339150.0 ; - RECT 70950.0 339600.0 71850.0 336750.0 ; - RECT 71400.0 340050.0 73800.0 339150.0 ; - RECT 73350.0 344250.0 74250.0 339600.0 ; - RECT 70800.0 336750.0 72000.0 335550.0 ; - RECT 73200.0 345450.0 74400.0 344250.0 ; - RECT 74400.0 340200.0 73200.0 339000.0 ; - RECT 53250.0 340200.0 54450.0 341400.0 ; - RECT 55200.0 342600.0 56400.0 343800.0 ; - RECT 72000.0 341700.0 70800.0 342900.0 ; - RECT 70950.0 354750.0 71850.0 355650.0 ; - RECT 73350.0 354750.0 74250.0 355650.0 ; - RECT 70950.0 355200.0 71850.0 358050.0 ; - RECT 71400.0 354750.0 73800.0 355650.0 ; - RECT 73350.0 350550.0 74250.0 355200.0 ; - RECT 70800.0 358050.0 72000.0 359250.0 ; - RECT 73200.0 349350.0 74400.0 350550.0 ; - RECT 74400.0 354600.0 73200.0 355800.0 ; - RECT 53250.0 353400.0 54450.0 354600.0 ; - RECT 55200.0 351000.0 56400.0 352200.0 ; - RECT 72000.0 351900.0 70800.0 353100.0 ; - RECT 70950.0 367650.0 71850.0 366750.0 ; - RECT 73350.0 367650.0 74250.0 366750.0 ; - RECT 70950.0 367200.0 71850.0 364350.0 ; - RECT 71400.0 367650.0 73800.0 366750.0 ; - RECT 73350.0 371850.0 74250.0 367200.0 ; - RECT 70800.0 364350.0 72000.0 363150.0 ; - RECT 73200.0 373050.0 74400.0 371850.0 ; - RECT 74400.0 367800.0 73200.0 366600.0 ; - RECT 53250.0 367800.0 54450.0 369000.0 ; - RECT 55200.0 370200.0 56400.0 371400.0 ; - RECT 72000.0 369300.0 70800.0 370500.0 ; - RECT 70950.0 382350.0 71850.0 383250.0 ; - RECT 73350.0 382350.0 74250.0 383250.0 ; - RECT 70950.0 382800.0 71850.0 385650.0 ; - RECT 71400.0 382350.0 73800.0 383250.0 ; - RECT 73350.0 378150.0 74250.0 382800.0 ; - RECT 70800.0 385650.0 72000.0 386850.0 ; - RECT 73200.0 376950.0 74400.0 378150.0 ; - RECT 74400.0 382200.0 73200.0 383400.0 ; - RECT 53250.0 381000.0 54450.0 382200.0 ; - RECT 55200.0 378600.0 56400.0 379800.0 ; - RECT 72000.0 379500.0 70800.0 380700.0 ; - RECT 70950.0 395250.0 71850.0 394350.0 ; - RECT 73350.0 395250.0 74250.0 394350.0 ; - RECT 70950.0 394800.0 71850.0 391950.0 ; - RECT 71400.0 395250.0 73800.0 394350.0 ; - RECT 73350.0 399450.0 74250.0 394800.0 ; - RECT 70800.0 391950.0 72000.0 390750.0 ; - RECT 73200.0 400650.0 74400.0 399450.0 ; - RECT 74400.0 395400.0 73200.0 394200.0 ; - RECT 53250.0 395400.0 54450.0 396600.0 ; - RECT 55200.0 397800.0 56400.0 399000.0 ; - RECT 72000.0 396900.0 70800.0 398100.0 ; - RECT 70950.0 409950.0 71850.0 410850.0 ; - RECT 73350.0 409950.0 74250.0 410850.0 ; - RECT 70950.0 410400.0 71850.0 413250.0 ; - RECT 71400.0 409950.0 73800.0 410850.0 ; - RECT 73350.0 405750.0 74250.0 410400.0 ; - RECT 70800.0 413250.0 72000.0 414450.0 ; - RECT 73200.0 404550.0 74400.0 405750.0 ; - RECT 74400.0 409800.0 73200.0 411000.0 ; - RECT 53250.0 408600.0 54450.0 409800.0 ; - RECT 55200.0 406200.0 56400.0 407400.0 ; - RECT 72000.0 407100.0 70800.0 408300.0 ; - RECT 70950.0 422850.0 71850.0 421950.0 ; - RECT 73350.0 422850.0 74250.0 421950.0 ; - RECT 70950.0 422400.0 71850.0 419550.0 ; - RECT 71400.0 422850.0 73800.0 421950.0 ; - RECT 73350.0 427050.0 74250.0 422400.0 ; - RECT 70800.0 419550.0 72000.0 418350.0 ; - RECT 73200.0 428250.0 74400.0 427050.0 ; - RECT 74400.0 423000.0 73200.0 421800.0 ; - RECT 53250.0 423000.0 54450.0 424200.0 ; - RECT 55200.0 425400.0 56400.0 426600.0 ; - RECT 72000.0 424500.0 70800.0 425700.0 ; - RECT 53400.0 209400.0 54300.0 430200.0 ; - RECT 9900.0 93600.0 69900.0 83400.0 ; - RECT 9900.0 73200.0 69900.0 83400.0 ; - RECT 9900.0 73200.0 69900.0 63000.0 ; - RECT 9900.0 52800.0 69900.0 63000.0 ; - RECT 67500.0 89100.0 68700.0 86400.0 ; - RECT 65400.0 91800.0 69900.0 90600.0 ; - RECT 67500.0 80400.0 68700.0 77700.0 ; - RECT 65400.0 76200.0 69900.0 75000.0 ; - RECT 67500.0 68700.0 68700.0 66000.0 ; - RECT 65400.0 71400.0 69900.0 70200.0 ; - RECT 67500.0 60000.0 68700.0 57300.0 ; - RECT 65400.0 55800.0 69900.0 54600.0 ; - RECT 9900.0 84000.0 69900.0 82800.0 ; - RECT 9900.0 63600.0 69900.0 62400.0 ; - RECT 0.0 0.0 3600.0 3600.0 ; - RECT 0.0 453300.0 3600.0 456900.0 ; - RECT 139500.0 0.0 143100.0 3600.0 ; - RECT 139500.0 453300.0 143100.0 456900.0 ; - RECT 4950.0 4950.0 8550.0 8550.0 ; - RECT 4950.0 458250.0 8550.0 461850.0 ; - RECT 144450.0 4950.0 148050.0 8550.0 ; - RECT 144450.0 458250.0 148050.0 461850.0 ; - RECT 117450.0 15750.0 118650.0 16950.0 ; - RECT 127650.0 15750.0 128850.0 16950.0 ; - RECT 121200.0 300.0 122400.0 1500.0 ; - RECT 131400.0 300.0 132600.0 1500.0 ; - RECT 81300.0 101250.0 80100.0 102450.0 ; - RECT 86400.0 101100.0 85200.0 102300.0 ; - RECT 78300.0 115050.0 77100.0 116250.0 ; - RECT 89100.0 114900.0 87900.0 116100.0 ; - RECT 81300.0 156450.0 80100.0 157650.0 ; - RECT 91800.0 156300.0 90600.0 157500.0 ; - RECT 78300.0 170250.0 77100.0 171450.0 ; - RECT 94500.0 170100.0 93300.0 171300.0 ; - RECT 3600.0 98400.0 -5.3290705182e-12 99600.0 ; - RECT 3600.0 126000.0 -5.3290705182e-12 127200.0 ; - RECT 3600.0 153600.0 -5.3290705182e-12 154800.0 ; - RECT 3600.0 181200.0 -5.3290705182e-12 182400.0 ; - RECT 8550.0 112200.0 4950.0 113400.0 ; - RECT 8550.0 139800.0 4950.0 141000.0 ; - RECT 8550.0 167400.0 4950.0 168600.0 ; - RECT 8550.0 195000.0 4950.0 196200.0 ; - RECT 69300.0 87150.0 68100.0 88350.0 ; - RECT 86400.0 87150.0 85200.0 88350.0 ; - RECT 69300.0 78450.0 68100.0 79650.0 ; - RECT 89100.0 78450.0 87900.0 79650.0 ; - RECT 69300.0 66750.0 68100.0 67950.0 ; - RECT 91800.0 66750.0 90600.0 67950.0 ; - RECT 69300.0 58050.0 68100.0 59250.0 ; - RECT 94500.0 58050.0 93300.0 59250.0 ; - RECT 11100.0 82800.0 9900.0 84000.0 ; - RECT 3600.0 82800.0 -5.3290705182e-12 84000.0 ; - RECT 11100.0 62400.0 9900.0 63600.0 ; - RECT 3600.0 62400.0 -5.3290705182e-12 63600.0 ; - RECT 8550.0 50100.0 4950.0 51300.0 ; - RECT 105300.0 42150.0 104100.0 43350.0 ; - RECT 99900.0 37650.0 98700.0 38850.0 ; - RECT 102600.0 35250.0 101400.0 36450.0 ; - RECT 105300.0 438450.0 104100.0 439650.0 ; - RECT 108000.0 106950.0 106800.0 108150.0 ; - RECT 110700.0 205050.0 109500.0 206250.0 ; - RECT 97200.0 95100.0 96000.0 96300.0 ; - RECT 54450.0 431700.0 53250.0 432900.0 ; - RECT 97200.0 431700.0 96000.0 432900.0 ; - RECT 148050.0 449550.0 144450.0 450750.0 ; - RECT 148050.0 177750.0 144450.0 178950.0 ; - RECT 148050.0 109050.0 144450.0 110250.0 ; - RECT 148050.0 96150.0 144450.0 97350.0 ; - RECT 148050.0 19350.0 144450.0 20550.0 ; - RECT 8550.0 222600.0 4950.0 223800.0 ; - RECT 148050.0 222600.0 144450.0 223800.0 ; - RECT 8550.0 250200.0 4950.0 251400.0 ; - RECT 148050.0 250200.0 144450.0 251400.0 ; - RECT 8550.0 277800.0 4950.0 279000.0 ; - RECT 148050.0 277800.0 144450.0 279000.0 ; - RECT 8550.0 305400.0 4950.0 306600.0 ; - RECT 148050.0 305400.0 144450.0 306600.0 ; - RECT 8550.0 333000.0 4950.0 334200.0 ; - RECT 148050.0 333000.0 144450.0 334200.0 ; - RECT 8550.0 360600.0 4950.0 361800.0 ; - RECT 148050.0 360600.0 144450.0 361800.0 ; - RECT 8550.0 388200.0 4950.0 389400.0 ; - RECT 148050.0 388200.0 144450.0 389400.0 ; - RECT 8550.0 415800.0 4950.0 417000.0 ; - RECT 148050.0 415800.0 144450.0 417000.0 ; - RECT 143100.0 33150.0 139500.0 34350.0 ; - RECT 143100.0 202950.0 139500.0 204150.0 ; - RECT 143100.0 104850.0 139500.0 106050.0 ; - RECT 3600.0 208800.0 -5.3290705182e-12 210000.0 ; - RECT 3600.0 236400.0 -5.3290705182e-12 237600.0 ; - RECT 3600.0 264000.0 -5.3290705182e-12 265200.0 ; - RECT 3600.0 291600.0 -5.3290705182e-12 292800.0 ; - RECT 3600.0 319200.0 -5.3290705182e-12 320400.0 ; - RECT 3600.0 346800.0 -5.3290705182e-12 348000.0 ; - RECT 3600.0 374400.0 -5.3290705182e-12 375600.0 ; - RECT 3600.0 402000.0 -5.3290705182e-12 403200.0 ; - RECT 3600.0 429600.0 -5.3290705182e-12 430800.0 ; - RECT 120900.0 0.0 121800.0 1800.0 ; - RECT 131100.0 0.0 132000.0 1800.0 ; - RECT 109650.0 0.0 110550.0 461850.0 ; - RECT 106950.0 0.0 107850.0 461850.0 ; - RECT 98850.0 0.0 99750.0 461850.0 ; - RECT 101550.0 0.0 102450.0 461850.0 ; - RECT 104250.0 0.0 105150.0 461850.0 ; - RECT 96150.0 0.0 97050.0 461850.0 ; - RECT 4950.0 0.0 8550.0 461850.0 ; - RECT 144450.0 0.0 148050.0 461850.0 ; - RECT 0.0 0.0 3600.0 461850.0 ; - RECT 139500.0 0.0 143100.0 461850.0 ; - RECT -3000.0 269400.0 -52800.0 270300.0 ; - RECT -3000.0 272100.0 -52800.0 273000.0 ; - RECT -3000.0 274800.0 -52800.0 275700.0 ; - RECT -3000.0 280200.0 -52800.0 281100.0 ; - RECT -9450.0 223050.0 -16800.0 223950.0 ; - RECT -19050.0 184650.0 -19950.0 264450.0 ; - RECT -3000.0 266700.0 -5700.0 267600.0 ; - RECT -14100.0 277500.0 -16800.0 278400.0 ; - RECT -27900.0 266700.0 -30600.0 267600.0 ; - RECT -41700.0 277500.0 -44400.0 278400.0 ; - RECT -52800.0 181800.0 -42600.0 241800.0 ; - RECT -32400.0 181800.0 -42600.0 241800.0 ; - RECT -32400.0 181800.0 -22200.0 241800.0 ; - RECT -48300.0 239400.0 -45600.0 240600.0 ; - RECT -51000.0 237300.0 -49800.0 241800.0 ; - RECT -39600.0 239400.0 -36900.0 240600.0 ; - RECT -35400.0 237300.0 -34200.0 241800.0 ; - RECT -27900.0 239400.0 -25200.0 240600.0 ; - RECT -30600.0 237300.0 -29400.0 241800.0 ; - RECT -43200.0 181800.0 -42000.0 241800.0 ; - RECT -22800.0 181800.0 -21600.0 241800.0 ; - RECT -6150.0 297450.0 -13650.0 298350.0 ; - RECT -11100.0 292650.0 -12000.0 293550.0 ; - RECT -11100.0 297450.0 -12000.0 298350.0 ; - RECT -11550.0 292650.0 -13650.0 293550.0 ; - RECT -11100.0 293100.0 -12000.0 297900.0 ; - RECT -6150.0 297450.0 -11550.0 298350.0 ; - RECT -13650.0 292500.0 -14850.0 293700.0 ; - RECT -13650.0 297300.0 -14850.0 298500.0 ; - RECT -4950.0 297300.0 -6150.0 298500.0 ; - RECT -10950.0 297300.0 -12150.0 298500.0 ; - RECT -24000.0 295050.0 -23100.0 295950.0 ; - RECT -23550.0 295050.0 -20550.0 295950.0 ; - RECT -24000.0 295500.0 -23100.0 296400.0 ; - RECT -29100.0 295050.0 -28200.0 295950.0 ; - RECT -29100.0 293700.0 -28200.0 295500.0 ; - RECT -28650.0 295050.0 -23550.0 295950.0 ; - RECT -20550.0 294900.0 -19350.0 296100.0 ; - RECT -29250.0 293700.0 -28050.0 292500.0 ; - RECT -24150.0 297000.0 -22950.0 295800.0 ; - RECT -23250.0 309750.0 -22350.0 310650.0 ; - RECT -23250.0 312150.0 -22350.0 313050.0 ; - RECT -22800.0 309750.0 -19950.0 310650.0 ; - RECT -23250.0 310200.0 -22350.0 312600.0 ; - RECT -27450.0 312150.0 -22800.0 313050.0 ; - RECT -19950.0 309600.0 -18750.0 310800.0 ; - RECT -28650.0 312000.0 -27450.0 313200.0 ; - RECT -23400.0 313200.0 -22200.0 312000.0 ; - RECT -33750.0 307050.0 -41250.0 307950.0 ; - RECT -38700.0 302250.0 -39600.0 303150.0 ; - RECT -38700.0 307050.0 -39600.0 307950.0 ; - RECT -39150.0 302250.0 -41250.0 303150.0 ; - RECT -38700.0 302700.0 -39600.0 307500.0 ; - RECT -33750.0 307050.0 -39150.0 307950.0 ; - RECT -41250.0 302100.0 -42450.0 303300.0 ; - RECT -41250.0 306900.0 -42450.0 308100.0 ; - RECT -32550.0 306900.0 -33750.0 308100.0 ; - RECT -38550.0 306900.0 -39750.0 308100.0 ; - RECT -49800.0 242400.0 -51000.0 241200.0 ; - RECT -49800.0 281250.0 -51000.0 280050.0 ; - RECT -46350.0 241200.0 -47550.0 240000.0 ; - RECT -46350.0 270450.0 -47550.0 269250.0 ; - RECT -34200.0 242400.0 -35400.0 241200.0 ; - RECT -34200.0 273150.0 -35400.0 271950.0 ; - RECT -29400.0 242400.0 -30600.0 241200.0 ; - RECT -29400.0 275850.0 -30600.0 274650.0 ; - RECT -42000.0 242400.0 -43200.0 241200.0 ; - RECT -42000.0 267750.0 -43200.0 266550.0 ; - RECT -21600.0 242400.0 -22800.0 241200.0 ; - RECT -21600.0 267750.0 -22800.0 266550.0 ; - RECT -30150.0 351300.0 -31050.0 437100.0 ; - RECT -35550.0 351300.0 -36450.0 432300.0 ; - RECT -45750.0 351300.0 -46650.0 432300.0 ; - RECT -32400.0 355500.0 -33300.0 363600.0 ; - RECT -39150.0 355500.0 -40050.0 360300.0 ; - RECT -10050.0 395100.0 -9150.0 402300.0 ; - RECT -10050.0 402300.0 -9150.0 411900.0 ; - RECT -10050.0 411900.0 -9150.0 421500.0 ; - RECT -10050.0 423900.0 -9150.0 431100.0 ; - RECT -10050.0 431100.0 -9150.0 440700.0 ; - RECT -10050.0 440700.0 -9150.0 450300.0 ; - RECT -17250.0 452250.0 -16350.0 453150.0 ; - RECT -17250.0 443850.0 -16350.0 444750.0 ; - RECT -16800.0 452250.0 -9600.0 453150.0 ; - RECT -17250.0 444300.0 -16350.0 452700.0 ; - RECT -24000.0 443850.0 -16800.0 444750.0 ; - RECT -24450.0 434700.0 -23550.0 444300.0 ; - RECT -24450.0 425100.0 -23550.0 434700.0 ; - RECT -24450.0 415500.0 -23550.0 422700.0 ; - RECT -24450.0 405900.0 -23550.0 415500.0 ; - RECT -24450.0 396300.0 -23550.0 405900.0 ; - RECT -10200.0 401700.0 -9000.0 402900.0 ; - RECT -10200.0 411300.0 -9000.0 412500.0 ; - RECT -10200.0 420900.0 -9000.0 422100.0 ; - RECT -10200.0 430500.0 -9000.0 431700.0 ; - RECT -10200.0 440100.0 -9000.0 441300.0 ; - RECT -10200.0 449700.0 -9000.0 450900.0 ; - RECT -24600.0 443700.0 -23400.0 444900.0 ; - RECT -24600.0 434100.0 -23400.0 435300.0 ; - RECT -24600.0 424500.0 -23400.0 425700.0 ; - RECT -24600.0 414900.0 -23400.0 416100.0 ; - RECT -24600.0 405300.0 -23400.0 406500.0 ; - RECT -24600.0 395700.0 -23400.0 396900.0 ; - RECT -10200.0 394500.0 -9000.0 395700.0 ; - RECT -10200.0 423300.0 -9000.0 424500.0 ; - RECT -10200.0 452100.0 -9000.0 453300.0 ; - RECT -24600.0 422100.0 -23400.0 423300.0 ; - RECT -36000.0 374700.0 -46200.0 360900.0 ; - RECT -36000.0 374700.0 -46200.0 388500.0 ; - RECT -36000.0 402300.0 -46200.0 388500.0 ; - RECT -36000.0 402300.0 -46200.0 416100.0 ; - RECT -36000.0 429900.0 -46200.0 416100.0 ; - RECT -39000.0 375300.0 -40200.0 433500.0 ; - RECT -42000.0 374100.0 -43200.0 432300.0 ; - RECT -35400.0 374100.0 -36600.0 432300.0 ; - RECT -45600.0 374100.0 -46800.0 432300.0 ; - RECT -30450.0 376200.0 -31650.0 377400.0 ; - RECT -30450.0 399600.0 -31650.0 400800.0 ; - RECT -30450.0 403800.0 -31650.0 405000.0 ; - RECT -30450.0 427200.0 -31650.0 428400.0 ; - RECT -30600.0 389700.0 -31800.0 390900.0 ; - RECT -30000.0 350100.0 -31200.0 351300.0 ; - RECT -36600.0 350700.0 -35400.0 351900.0 ; - RECT -46800.0 350700.0 -45600.0 351900.0 ; - RECT -33450.0 363000.0 -32250.0 364200.0 ; - RECT -33450.0 354900.0 -32250.0 356100.0 ; - RECT -40200.0 354900.0 -39000.0 356100.0 ; - RECT -8850.0 265050.0 -10050.0 263850.0 ; - RECT -8850.0 224100.0 -10050.0 222900.0 ; - RECT -16200.0 224100.0 -17400.0 222900.0 ; - RECT -16200.0 283950.0 -17400.0 282750.0 ; - RECT -18900.0 185250.0 -20100.0 184050.0 ; - RECT -22950.0 265050.0 -24150.0 263850.0 ; - RECT -25650.0 270450.0 -26850.0 269250.0 ; - RECT -22200.0 307800.0 -23400.0 306600.0 ; - RECT -22200.0 307800.0 -23400.0 306600.0 ; - RECT -22200.0 283950.0 -23400.0 282750.0 ; - RECT -24900.0 310800.0 -26100.0 309600.0 ; - RECT -24900.0 310800.0 -26100.0 309600.0 ; - RECT -24900.0 281250.0 -26100.0 280050.0 ; - RECT -10950.0 283950.0 -12150.0 282750.0 ; - RECT -9000.0 281250.0 -10200.0 280050.0 ; - RECT -7050.0 273150.0 -8250.0 271950.0 ; - RECT -38550.0 283950.0 -39750.0 282750.0 ; - RECT -36600.0 273150.0 -37800.0 271950.0 ; - RECT -34650.0 275850.0 -35850.0 274650.0 ; - RECT -22950.0 302100.0 -24150.0 303300.0 ; - RECT -22200.0 319200.0 -23400.0 320400.0 ; - RECT -36600.0 341700.0 -37800.0 342900.0 ; - RECT -23400.0 321900.0 -24600.0 323100.0 ; - RECT -2400.0 267750.0 -3600.0 266550.0 ; - RECT -16200.0 278550.0 -17400.0 277350.0 ; - RECT -30000.0 267750.0 -31200.0 266550.0 ; - RECT -43800.0 278550.0 -45000.0 277350.0 ; - RECT -3000.0 322050.0 -24000.0 322950.0 ; - RECT -3000.0 341850.0 -37200.0 342750.0 ; - RECT -3000.0 302250.0 -23550.0 303150.0 ; - RECT -3000.0 319350.0 -22800.0 320250.0 ; - RECT -3000.0 282900.0 -52800.0 283800.0 ; - RECT -3000.0 264000.0 -52800.0 264900.0 ; - RECT -3000.0 277500.0 -52800.0 278400.0 ; - RECT -3000.0 266700.0 -52800.0 267600.0 ; - RECT 110700.0 321900.0 109500.0 323100.0 ; - RECT -3300.0 322050.0 -4500.0 323250.0 ; - RECT 108000.0 341700.0 106800.0 342900.0 ; - RECT -3300.0 341850.0 -4500.0 343050.0 ; - RECT 102600.0 302100.0 101400.0 303300.0 ; - RECT -3300.0 302250.0 -4500.0 303450.0 ; - RECT 99900.0 319200.0 98700.0 320400.0 ; - RECT -3300.0 319350.0 -4500.0 320550.0 ; - RECT 105300.0 282750.0 104100.0 283950.0 ; - RECT -3300.0 282900.0 -4500.0 284100.0 ; - RECT 97200.0 263850.0 96000.0 265050.0 ; - RECT -3300.0 264000.0 -4500.0 265200.0 ; - RECT 7350.0 277350.0 6150.0 278550.0 ; - RECT -3300.0 277500.0 -4500.0 278700.0 ; - LAYER metal3 ; - RECT -3000.0 321750.0 110100.0 323250.0 ; - RECT -3000.0 341550.0 107400.0 343050.0 ; - RECT -3000.0 301950.0 102000.0 303450.0 ; - RECT -3000.0 319050.0 99300.0 320550.0 ; - RECT -3000.0 282600.0 104700.0 284100.0 ; - RECT -3000.0 263700.0 96600.0 265200.0 ; - RECT -3000.0 277200.0 6750.0 278700.0 ; - RECT 117150.0 16200.0 118650.0 161400.0 ; - RECT 127350.0 16200.0 128850.0 161400.0 ; - RECT 120900.0 0.0 122400.0 39900.0 ; - RECT 131100.0 0.0 132600.0 39900.0 ; - RECT 117000.0 161400.0 118800.0 163200.0 ; - RECT 127200.0 161400.0 129000.0 163200.0 ; - RECT 120600.0 40800.0 122400.0 42600.0 ; - RECT 130800.0 40800.0 132600.0 42600.0 ; - RECT 10800.0 89400.0 12600.0 87600.0 ; - RECT 10800.0 79200.0 12600.0 77400.0 ; - RECT 10800.0 69000.0 12600.0 67200.0 ; - RECT 10800.0 58800.0 12600.0 57000.0 ; - RECT 117150.0 15450.0 118950.0 17250.0 ; - RECT 127350.0 15450.0 129150.0 17250.0 ; - RECT 120900.0 0.0 122700.0 1800.0 ; - RECT 131100.0 0.0 132900.0 1800.0 ; - RECT 0.0 87600.0 10800.0 89100.0 ; - RECT 0.0 77400.0 10800.0 78900.0 ; - RECT 0.0 67200.0 10800.0 68700.0 ; - RECT 0.0 57000.0 10800.0 58500.0 ; - RECT -49650.0 241800.0 -51150.0 280650.0 ; - RECT -46200.0 240600.0 -47700.0 269850.0 ; - RECT -34050.0 241800.0 -35550.0 272550.0 ; - RECT -29250.0 241800.0 -30750.0 275250.0 ; - RECT -41850.0 241800.0 -43350.0 267150.0 ; - RECT -21450.0 241800.0 -22950.0 267150.0 ; - RECT -16050.0 223500.0 -17550.0 283350.0 ; - RECT -22050.0 283350.0 -23550.0 307200.0 ; - RECT -24750.0 280650.0 -26250.0 310200.0 ; - RECT -48600.0 182700.0 -46800.0 184500.0 ; - RECT -38400.0 182700.0 -36600.0 184500.0 ; - RECT -28200.0 182700.0 -26400.0 184500.0 ; - RECT -49500.0 242700.0 -51300.0 240900.0 ; - RECT -49500.0 281550.0 -51300.0 279750.0 ; - RECT -46050.0 241500.0 -47850.0 239700.0 ; - RECT -46050.0 270750.0 -47850.0 268950.0 ; - RECT -33900.0 242700.0 -35700.0 240900.0 ; - RECT -33900.0 273450.0 -35700.0 271650.0 ; - RECT -29100.0 242700.0 -30900.0 240900.0 ; - RECT -29100.0 276150.0 -30900.0 274350.0 ; - RECT -41700.0 242700.0 -43500.0 240900.0 ; - RECT -41700.0 268050.0 -43500.0 266250.0 ; - RECT -21300.0 242700.0 -23100.0 240900.0 ; - RECT -21300.0 268050.0 -23100.0 266250.0 ; - RECT -15900.0 224400.0 -17700.0 222600.0 ; - RECT -15900.0 284250.0 -17700.0 282450.0 ; - RECT -21900.0 308100.0 -23700.0 306300.0 ; - RECT -21900.0 284250.0 -23700.0 282450.0 ; - RECT -24600.0 311100.0 -26400.0 309300.0 ; - RECT -24600.0 281550.0 -26400.0 279750.0 ; - RECT -36600.0 182700.0 -38400.0 184500.0 ; - RECT -26400.0 182700.0 -28200.0 184500.0 ; - RECT -46800.0 182700.0 -48600.0 184500.0 ; - RECT 111000.0 321600.0 109200.0 323400.0 ; - RECT -3000.0 321750.0 -4800.0 323550.0 ; - RECT 108300.0 341400.0 106500.0 343200.0 ; - RECT -3000.0 341550.0 -4800.0 343350.0 ; - RECT 102900.0 301800.0 101100.0 303600.0 ; - RECT -3000.0 301950.0 -4800.0 303750.0 ; - RECT 100200.0 318900.0 98400.0 320700.0 ; - RECT -3000.0 319050.0 -4800.0 320850.0 ; - RECT 105600.0 282450.0 103800.0 284250.0 ; - RECT -3000.0 282600.0 -4800.0 284400.0 ; - RECT 97500.0 263550.0 95700.0 265350.0 ; - RECT -3000.0 263700.0 -4800.0 265500.0 ; - RECT 7650.0 277050.0 5850.0 278850.0 ; - RECT -3000.0 277200.0 -4800.0 279000.0 ; - END - END sram_2_16_1_scn3me_subm -END LIBRARY diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm.sp b/compiler/tests/golden/sram_2_16_1_scn3me_subm.sp deleted file mode 100644 index 258b4464..00000000 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm.sp +++ /dev/null @@ -1,681 +0,0 @@ -* OpenRAM generated memory. -* User: mrg -.global vdd gnd -*master-slave flip-flop with both output and inverted ouput - -.subckt ms_flop din dout dout_bar clk vdd gnd -xmaster din mout mout_bar clk clk_bar vdd gnd dlatch -xslave mout_bar dout_bar dout clk_bar clk_nn vdd gnd dlatch -.ends flop - -.subckt dlatch din dout dout_bar clk clk_bar vdd gnd -*clk inverter -mPff1 clk_bar clk vdd vdd p W=1.8u L=0.6u m=1 -mNff1 clk_bar clk gnd gnd n W=0.9u L=0.6u m=1 - -*transmission gate 1 -mtmP1 din clk int1 vdd p W=1.8u L=0.6u m=1 -mtmN1 din clk_bar int1 gnd n W=0.9u L=0.6u m=1 - -*foward inverter -mPff3 dout_bar int1 vdd vdd p W=1.8u L=0.6u m=1 -mNff3 dout_bar int1 gnd gnd n W=0.9u L=0.6u m=1 - -*backward inverter -mPff4 dout dout_bar vdd vdd p W=1.8u L=0.6u m=1 -mNf4 dout dout_bar gnd gnd n W=0.9u L=0.6u m=1 - -*transmission gate 2 -mtmP2 int1 clk_bar dout vdd p W=1.8u L=0.6u m=1 -mtmN2 int1 clk dout gnd n W=0.9u L=0.6u m=1 -.ends dlatch - - -.SUBCKT inv_nmos11 D G S B -Mnmos D G S B n m=1 w=1.2u l=0.6u -.ENDS inv_nmos11 - -.SUBCKT inv_pmos12 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS inv_pmos12 - -.SUBCKT pinv A Z vdd gnd -Xpinv_nmos Z A gnd gnd inv_nmos11 -Xpinv_pmos Z A vdd vdd inv_pmos12 -.ENDS pinv - -.SUBCKT nand_2_nmos13 D G S B -Mnmos D G S B n m=1 w=2.4u l=0.6u -.ENDS nand_2_nmos13 - -.SUBCKT nand_2_nmos24 D G S B -Mnmos D G S B n m=1 w=2.4u l=0.6u -.ENDS nand_2_nmos24 - -.SUBCKT nand_2_pmos15 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS nand_2_pmos15 - -.SUBCKT nand_2_pmos26 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS nand_2_pmos26 - -.SUBCKT nand2 A B Z vdd gnd -Xnmos1 Z A net1 gnd nand_2_nmos13 -Xnmos2 net1 B gnd gnd nand_2_nmos24 -Xpmos1 vdd A Z vdd nand_2_pmos15 -Xpmos2 Z B vdd vdd nand_2_pmos26 -.ENDS nand2 - -.SUBCKT nand_3_nmos17 D G S B -Mnmos D G S B n m=1 w=3.6u l=0.6u -.ENDS nand_3_nmos17 - -.SUBCKT nand_3_nmos28 D G S B -Mnmos D G S B n m=1 w=3.6u l=0.6u -.ENDS nand_3_nmos28 - -.SUBCKT nand_3_nmos39 D G S B -Mnmos D G S B n m=1 w=3.6u l=0.6u -.ENDS nand_3_nmos39 - -.SUBCKT nand_3_pmos110 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS nand_3_pmos110 - -.SUBCKT nand_3_pmos211 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS nand_3_pmos211 - -.SUBCKT nand_3_pmos312 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS nand_3_pmos312 - -.SUBCKT NAND3 A B C Z vdd gnd -Xnmos1 net2 A gnd gnd nand_3_nmos17 -Xnmos2 net1 B net2 gnd nand_3_nmos28 -Xnmos3 Z C net1 gnd nand_3_nmos39 -Xpmos1 Z A vdd vdd nand_3_pmos110 -Xpmos2 vdd B Z vdd nand_3_pmos211 -Xpmos3 Z C vdd vdd nand_3_pmos312 -.ENDS NAND3 - -.SUBCKT inv_nmos113 D G S B -Mnmos D G S B n m=4 w=1.2u l=0.6u -.ENDS inv_nmos113 - -.SUBCKT inv_pmos114 D G S B -Mpmos D G S B p m=4 w=2.4u l=0.6u -.ENDS inv_pmos114 - -.SUBCKT pinv4 A Z vdd gnd -Xpinv_nmos Z A gnd gnd inv_nmos113 -Xpinv_pmos Z A vdd vdd inv_pmos114 -.ENDS pinv4 - -.SUBCKT nor_2_nmos123 D G S B -Mnmos D G S B n m=1 w=1.2u l=0.6u -.ENDS nor_2_nmos123 - -.SUBCKT nor_2_nmos224 D G S B -Mnmos D G S B n m=1 w=1.2u l=0.6u -.ENDS nor_2_nmos224 - -.SUBCKT nor_2_pmos125 D G S B -Mpmos D G S B p m=4 w=1.2u l=0.6u -.ENDS nor_2_pmos125 - -.SUBCKT nor_2_pmos226 D G S B -Mpmos D G S B p m=4 w=1.2u l=0.6u -.ENDS nor_2_pmos226 - -.SUBCKT nor2 A B Z vdd gnd -Xnmos1 Z A gnd gnd nor_2_nmos123 -Xnmos2 Z B gnd gnd nor_2_nmos224 -Xpmos1 vdd A net1 vdd nor_2_pmos125 -Xpmos2 net1 B Z vdd nor_2_pmos226 -.ENDS nor2 - -.SUBCKT msf_control DATA[0] DATA[1] DATA[2] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] data_in[2] data_in_bar[2] clk vdd gnd -XXdff0 DATA[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop -XXdff1 DATA[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop -XXdff2 DATA[2] data_in[2] data_in_bar[2] clk vdd gnd ms_flop -.ENDS msf_control - -*********************** "cell_6t" ****************************** -.SUBCKT replica_cell_6t bl br wl vdd gnd -M_1 gnd net_2 vdd vdd p W='0.9u' L=1.2u -M_2 net_2 gnd 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 gnd gnd n W='1.2u' L=0.6u -M_5 net_2 gnd gnd gnd n W='2.4u' L=0.6u -M_6 gnd net_2 gnd gnd n W='2.4u' L=0.6u -.ENDS $ replica_cell_6t - -*********************** "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 -.ENDS $ cell_6t - -.SUBCKT bitline_load bl[0] br[0] wl[0] wl[1] vdd gnd -Xbit_r0_c0 bl[0] br[0] wl[0] vdd gnd cell_6t -Xbit_r1_c0 bl[0] br[0] wl[1] vdd gnd cell_6t -.ENDS bitline_load - -.SUBCKT inv_nmos127 D G S B -Mnmos D G S B n m=1 w=1.2u l=0.6u -.ENDS inv_nmos127 - -.SUBCKT inv_pmos128 D G S B -Mpmos D G S B p m=1 w=3.6u l=0.6u -.ENDS inv_pmos128 - -.SUBCKT delay_chain_inv A Z vdd gnd -Xpinv_nmos Z A gnd gnd inv_nmos127 -Xpinv_pmos Z A vdd vdd inv_pmos128 -.ENDS delay_chain_inv - -.SUBCKT delay_chain clk_in clk_out vdd gnd -Xinv_chain0 clk_in s1 vdd gnd delay_chain_inv -Xinv_chain1 s1 s2 vdd gnd delay_chain_inv -Xinv_chain2 s2 s3 vdd gnd delay_chain_inv -Xinv_chain3 s3 clk_out vdd gnd delay_chain_inv -.ENDS delay_chain - -.SUBCKT inv_nmos129 D G S B -Mnmos D G S B n m=1 w=1.2u l=0.6u -.ENDS inv_nmos129 - -.SUBCKT inv_pmos130 D G S B -Mpmos D G S B p m=1 w=3.6u l=0.6u -.ENDS inv_pmos130 - -.SUBCKT RBL_inv A Z vdd gnd -Xpinv_nmos Z A gnd gnd inv_nmos129 -Xpinv_pmos Z A vdd vdd inv_pmos130 -.ENDS RBL_inv - -.SUBCKT nor_2_nmos139 D G S B -Mnmos D G S B n m=1 w=1.2u l=0.6u -.ENDS nor_2_nmos139 - -.SUBCKT nor_2_nmos240 D G S B -Mnmos D G S B n m=1 w=1.2u l=0.6u -.ENDS nor_2_nmos240 - -.SUBCKT nor_2_pmos141 D G S B -Mpmos D G S B p m=4 w=1.2u l=0.6u -.ENDS nor_2_pmos141 - -.SUBCKT nor_2_pmos242 D G S B -Mpmos D G S B p m=4 w=1.2u l=0.6u -.ENDS nor_2_pmos242 - -.SUBCKT replica_bitline_nor2 A B Z vdd gnd -Xnmos1 Z A gnd gnd nor_2_nmos139 -Xnmos2 Z B gnd gnd nor_2_nmos240 -Xpmos1 vdd A net1 vdd nor_2_pmos141 -Xpmos2 net1 B Z vdd nor_2_pmos242 -.ENDS replica_bitline_nor2 - -.SUBCKT access_tx43 D G S B -Mpmos D G S B p m=1 w=1.2u l=0.6u -.ENDS access_tx43 - -.SUBCKT replica_bitline en out vdd gnd -XBL_inv bl[0] out vdd gnd RBL_inv -XBL_access_tx vdd delayed_en bl[0] vdd access_tx43 -Xdelay_chain en delayed_en vdd gnd delay_chain -Xbitcell bl[0] br[0] delayed_en vdd gnd replica_cell_6t -Xload bl[0] br[0] gnd gnd vdd gnd bitline_load -.ENDS replica_bitline - -.SUBCKT control_logic CSb WEb OEb s_en w_en tri_en tri_en_bar clk_bar clk vdd gnd -Xmsf_control CSb WEb OEb CS_bar CS WE_bar WE OE_bar OE clk vdd gnd msf_control -Xclk_inverter clk clk_bar vdd gnd pinv4 -Xnor2 clk OE_bar tri_en vdd gnd nor2 -Xnand2_tri_en OE clk_bar tri_en_bar vdd gnd nand2 -Xreplica_bitline rblk pre_s_en vdd gnd replica_bitline -Xinv_s_en1 pre_s_en_bar s_en vdd gnd pinv -Xinv_s_en2 pre_s_en pre_s_en_bar vdd gnd pinv -XNAND3_rblk_bar clk_bar OE CS rblk_bar vdd gnd NAND3 -XNAND3_w_en_bar clk_bar WE CS w_en_bar vdd gnd NAND3 -Xinv_rblk rblk_bar rblk vdd gnd pinv -Xinv_w_en w_en_bar pre_w_en vdd gnd pinv -Xinv_w_en1 pre_w_en pre_w_en1 vdd gnd pinv -Xinv_w_en2 pre_w_en1 w_en vdd gnd pinv -.ENDS control_logic - -.SUBCKT bitcell_array bl[0] br[0] bl[1] br[1] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] vdd gnd -Xbit_r0_c0 bl[0] br[0] wl[0] vdd gnd cell_6t -Xbit_r1_c0 bl[0] br[0] wl[1] vdd gnd cell_6t -Xbit_r2_c0 bl[0] br[0] wl[2] vdd gnd cell_6t -Xbit_r3_c0 bl[0] br[0] wl[3] vdd gnd cell_6t -Xbit_r4_c0 bl[0] br[0] wl[4] vdd gnd cell_6t -Xbit_r5_c0 bl[0] br[0] wl[5] vdd gnd cell_6t -Xbit_r6_c0 bl[0] br[0] wl[6] vdd gnd cell_6t -Xbit_r7_c0 bl[0] br[0] wl[7] vdd gnd cell_6t -Xbit_r8_c0 bl[0] br[0] wl[8] vdd gnd cell_6t -Xbit_r9_c0 bl[0] br[0] wl[9] vdd gnd cell_6t -Xbit_r10_c0 bl[0] br[0] wl[10] vdd gnd cell_6t -Xbit_r11_c0 bl[0] br[0] wl[11] vdd gnd cell_6t -Xbit_r12_c0 bl[0] br[0] wl[12] vdd gnd cell_6t -Xbit_r13_c0 bl[0] br[0] wl[13] vdd gnd cell_6t -Xbit_r14_c0 bl[0] br[0] wl[14] vdd gnd cell_6t -Xbit_r15_c0 bl[0] br[0] wl[15] vdd gnd cell_6t -Xbit_r0_c1 bl[1] br[1] wl[0] vdd gnd cell_6t -Xbit_r1_c1 bl[1] br[1] wl[1] vdd gnd cell_6t -Xbit_r2_c1 bl[1] br[1] wl[2] vdd gnd cell_6t -Xbit_r3_c1 bl[1] br[1] wl[3] vdd gnd cell_6t -Xbit_r4_c1 bl[1] br[1] wl[4] vdd gnd cell_6t -Xbit_r5_c1 bl[1] br[1] wl[5] vdd gnd cell_6t -Xbit_r6_c1 bl[1] br[1] wl[6] vdd gnd cell_6t -Xbit_r7_c1 bl[1] br[1] wl[7] vdd gnd cell_6t -Xbit_r8_c1 bl[1] br[1] wl[8] vdd gnd cell_6t -Xbit_r9_c1 bl[1] br[1] wl[9] vdd gnd cell_6t -Xbit_r10_c1 bl[1] br[1] wl[10] vdd gnd cell_6t -Xbit_r11_c1 bl[1] br[1] wl[11] vdd gnd cell_6t -Xbit_r12_c1 bl[1] br[1] wl[12] vdd gnd cell_6t -Xbit_r13_c1 bl[1] br[1] wl[13] vdd gnd cell_6t -Xbit_r14_c1 bl[1] br[1] wl[14] vdd gnd cell_6t -Xbit_r15_c1 bl[1] br[1] wl[15] vdd gnd cell_6t -.ENDS bitcell_array - -.SUBCKT lower_pmos44 D G S B -Mpmos D G S B p m=1 w=1.2u l=0.6u -.ENDS lower_pmos44 - -.SUBCKT upper_pmos45 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS upper_pmos45 - -.SUBCKT precharge_cell bl br clk vdd -Xlower_pmos bl clk br vdd lower_pmos44 -Xupper_pmos1 bl clk vdd vdd upper_pmos45 -Xupper_pmos2 br clk vdd vdd upper_pmos45 -.ENDS precharge_cell - -.SUBCKT precharge_array bl[0] br[0] bl[1] br[1] clk vdd -Xpre_column_0 bl[0] br[0] clk vdd precharge_cell -Xpre_column_1 bl[1] br[1] clk vdd precharge_cell -.ENDS precharge_array -*********************** "sense_amp" ****************************** - -.SUBCKT sense_amp bl br dout sclk vdd gnd -M_1 dout net_1 vdd vdd p W='5.4*1u' L=0.6u -M_2 dout net_1 net_2 gnd n W='2.7*1u' L=0.6u -M_3 net_1 dout vdd vdd p W='5.4*1u' L=0.6u -M_4 net_1 dout net_2 gnd n W='2.7*1u' L=0.6u -M_5 bl sclk dout vdd p W='7.2*1u' L=0.6u -M_6 br sclk net_1 vdd p W='7.2*1u' L=0.6u -M_7 net_2 sclk gnd gnd n W='2.7*1u' L=0.6u -.ENDS sense_amp - - -.SUBCKT sense_amp_array bl[0] br[0] bl[1] br[1] data_out[0] data_out[1] sclk vdd gnd -Xsa_d0 bl[0] br[0] data_out[0] sclk vdd gnd sense_amp -Xsa_d1 bl[1] br[1] data_out[1] sclk vdd gnd sense_amp -.ENDS sense_amp_array -*********************** Write_Driver ****************************** -.SUBCKT write_driver din bl br wen 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 - -**** 2input nand gate follwed by inverter to drive BL ****** -M_3 net_2 wen 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 wen 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_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 - -**** 2input nand gate follwed by inverter to drive BR****** - -M_9 net_4 wen vdd vdd p W='2.1*1u' L=0.6u -M_10 net_4 wen 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_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_15 bl net_6 net_5 gnd n W='3.6*1u' L=0.6u -M_16 br net_1 net_5 gnd n W='3.6*1u' L=0.6u -M_17 net_5 wen gnd gnd n W='3.6*1u' L=0.6u - - - -.ENDS $ write_driver - - -.SUBCKT write_driver_array data_in[0] data_in[1] bl[0] br[0] bl[1] br[1] wen vdd gnd -XXwrite_driver0 data_in[0] bl[0] br[0] wen vdd gnd write_driver -XXwrite_driver1 data_in[1] bl[1] br[1] wen vdd gnd write_driver -.ENDS write_driver_array - -.SUBCKT inv_nmos147 D G S B -Mnmos D G S B n m=1 w=1.2u l=0.6u -.ENDS inv_nmos147 - -.SUBCKT inv_pmos148 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS inv_pmos148 - -.SUBCKT INVERTER A Z vdd gnd -Xpinv_nmos Z A gnd gnd inv_nmos147 -Xpinv_pmos Z A vdd vdd inv_pmos148 -.ENDS INVERTER - -.SUBCKT nand_2_nmos149 D G S B -Mnmos D G S B n m=1 w=2.4u l=0.6u -.ENDS nand_2_nmos149 - -.SUBCKT nand_2_nmos250 D G S B -Mnmos D G S B n m=1 w=2.4u l=0.6u -.ENDS nand_2_nmos250 - -.SUBCKT nand_2_pmos151 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS nand_2_pmos151 - -.SUBCKT nand_2_pmos252 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS nand_2_pmos252 - -.SUBCKT NAND2 A B Z vdd gnd -Xnmos1 Z A net1 gnd nand_2_nmos149 -Xnmos2 net1 B gnd gnd nand_2_nmos250 -Xpmos1 vdd A Z vdd nand_2_pmos151 -Xpmos2 Z B vdd vdd nand_2_pmos252 -.ENDS NAND2 - -.SUBCKT nand_2_nmos159 D G S B -Mnmos D G S B n m=1 w=2.4u l=0.6u -.ENDS nand_2_nmos159 - -.SUBCKT nand_2_nmos260 D G S B -Mnmos D G S B n m=1 w=2.4u l=0.6u -.ENDS nand_2_nmos260 - -.SUBCKT nand_2_pmos161 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS nand_2_pmos161 - -.SUBCKT nand_2_pmos262 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS nand_2_pmos262 - -.SUBCKT a_nand_2 A B Z vdd gnd -Xnmos1 Z A net1 gnd nand_2_nmos159 -Xnmos2 net1 B gnd gnd nand_2_nmos260 -Xpmos1 vdd A Z vdd nand_2_pmos161 -Xpmos2 Z B vdd vdd nand_2_pmos262 -.ENDS a_nand_2 - -.SUBCKT inv_nmos163 D G S B -Mnmos D G S B n m=1 w=1.2u l=0.6u -.ENDS inv_nmos163 - -.SUBCKT inv_pmos164 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS inv_pmos164 - -.SUBCKT a_inv_1 A Z vdd gnd -Xpinv_nmos Z A gnd gnd inv_nmos163 -Xpinv_pmos Z A vdd vdd inv_pmos164 -.ENDS a_inv_1 - -.SUBCKT pre2x4 A[0] A[1] out[0] out[1] out[2] out[3] vdd gnd -XXpre2x4_inv[0] A[0] B[0] vdd gnd a_inv_1 -XXpre2x4_inv[1] A[1] B[1] vdd gnd a_inv_1 -XXpre2x4_nand_inv[0] Z[0] out[0] vdd gnd a_inv_1 -XXpre2x4_nand_inv[1] Z[1] out[1] vdd gnd a_inv_1 -XXpre2x4_nand_inv[2] Z[2] out[2] vdd gnd a_inv_1 -XXpre2x4_nand_inv[3] Z[3] out[3] vdd gnd a_inv_1 -XXpre2x4_nand[0] A[0] A[1] Z[3] vdd gnd a_nand_2 -XXpre2x4_nand[1] B[0] A[1] Z[2] vdd gnd a_nand_2 -XXpre2x4_nand[2] A[0] B[1] Z[1] vdd gnd a_nand_2 -XXpre2x4_nand[3] B[0] B[1] Z[0] vdd gnd a_nand_2 -.ENDS pre2x4 - -.SUBCKT nand_3_nmos165 D G S B -Mnmos D G S B n m=1 w=3.6u l=0.6u -.ENDS nand_3_nmos165 - -.SUBCKT nand_3_nmos266 D G S B -Mnmos D G S B n m=1 w=3.6u l=0.6u -.ENDS nand_3_nmos266 - -.SUBCKT nand_3_nmos367 D G S B -Mnmos D G S B n m=1 w=3.6u l=0.6u -.ENDS nand_3_nmos367 - -.SUBCKT nand_3_pmos168 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS nand_3_pmos168 - -.SUBCKT nand_3_pmos269 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS nand_3_pmos269 - -.SUBCKT nand_3_pmos370 D G S B -Mpmos D G S B p m=1 w=2.4u l=0.6u -.ENDS nand_3_pmos370 - -.SUBCKT a_nand_3 A B C Z vdd gnd -Xnmos1 net2 A gnd gnd nand_3_nmos165 -Xnmos2 net1 B net2 gnd nand_3_nmos266 -Xnmos3 Z C net1 gnd nand_3_nmos367 -Xpmos1 Z A vdd vdd nand_3_pmos168 -Xpmos2 vdd B Z vdd nand_3_pmos269 -Xpmos3 Z C vdd vdd nand_3_pmos370 -.ENDS a_nand_3 - -.SUBCKT pre3x8 A[0] A[1] A[2] out[0] out[1] out[2] out[3] out[4] out[5] out[6] out[7] vdd gnd -XXpre2x4_inv[0] A[0] B[0] vdd gnd a_inv_1 -XXpre2x4_inv[1] A[1] B[1] vdd gnd a_inv_1 -XXpre2x4_inv[2] A[2] B[2] vdd gnd a_inv_1 -XXpre2x4_nand_inv[0] Z[0] out[0] vdd gnd a_inv_1 -XXpre2x4_nand_inv[1] Z[1] out[1] vdd gnd a_inv_1 -XXpre2x4_nand_inv[2] Z[2] out[2] vdd gnd a_inv_1 -XXpre2x4_nand_inv[3] Z[3] out[3] vdd gnd a_inv_1 -XXpre2x4_nand_inv[4] Z[4] out[4] vdd gnd a_inv_1 -XXpre2x4_nand_inv[5] Z[5] out[5] vdd gnd a_inv_1 -XXpre2x4_nand_inv[6] Z[6] out[6] vdd gnd a_inv_1 -XXpre2x4_nand_inv[7] Z[7] out[7] vdd gnd a_inv_1 -XXpre3x8_nand[0] A[0] A[1] A[2] Z[7] vdd gnd a_nand_3 -XXpre3x8_nand[1] A[0] A[1] B[2] Z[6] vdd gnd a_nand_3 -XXpre3x8_nand[2] A[0] B[1] A[2] Z[5] vdd gnd a_nand_3 -XXpre3x8_nand[3] A[0] B[1] B[2] Z[4] vdd gnd a_nand_3 -XXpre3x8_nand[4] B[0] A[1] A[2] Z[3] vdd gnd a_nand_3 -XXpre3x8_nand[5] B[0] A[1] B[2] Z[2] vdd gnd a_nand_3 -XXpre3x8_nand[6] B[0] B[1] A[2] Z[1] vdd gnd a_nand_3 -XXpre3x8_nand[7] B[0] B[1] B[2] Z[0] vdd gnd a_nand_3 -.ENDS pre3x8 - -.SUBCKT hierarchical_decoder A[0] A[1] A[2] A[3] decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] vdd gnd -Xpre[0] A[0] A[1] out[0] out[1] out[2] out[3] vdd gnd pre2x4 -Xpre[1] A[2] A[3] out[4] out[5] out[6] out[7] vdd gnd pre2x4 -XNAND2_[0] out[0] out[4] Z[0] vdd gnd NAND2 -XNAND2_[1] out[0] out[5] Z[1] vdd gnd NAND2 -XNAND2_[2] out[0] out[6] Z[2] vdd gnd NAND2 -XNAND2_[3] out[0] out[7] Z[3] vdd gnd NAND2 -XNAND2_[4] out[1] out[4] Z[4] vdd gnd NAND2 -XNAND2_[5] out[1] out[5] Z[5] vdd gnd NAND2 -XNAND2_[6] out[1] out[6] Z[6] vdd gnd NAND2 -XNAND2_[7] out[1] out[7] Z[7] vdd gnd NAND2 -XNAND2_[8] out[2] out[4] Z[8] vdd gnd NAND2 -XNAND2_[9] out[2] out[5] Z[9] vdd gnd NAND2 -XNAND2_[10] out[2] out[6] Z[10] vdd gnd NAND2 -XNAND2_[11] out[2] out[7] Z[11] vdd gnd NAND2 -XNAND2_[12] out[3] out[4] Z[12] vdd gnd NAND2 -XNAND2_[13] out[3] out[5] Z[13] vdd gnd NAND2 -XNAND2_[14] out[3] out[6] Z[14] vdd gnd NAND2 -XNAND2_[15] out[3] out[7] Z[15] vdd gnd NAND2 -XINVERTER_[0] Z[0] decode_out[0] vdd gnd INVERTER -XINVERTER_[1] Z[1] decode_out[1] vdd gnd INVERTER -XINVERTER_[2] Z[2] decode_out[2] vdd gnd INVERTER -XINVERTER_[3] Z[3] decode_out[3] vdd gnd INVERTER -XINVERTER_[4] Z[4] decode_out[4] vdd gnd INVERTER -XINVERTER_[5] Z[5] decode_out[5] vdd gnd INVERTER -XINVERTER_[6] Z[6] decode_out[6] vdd gnd INVERTER -XINVERTER_[7] Z[7] decode_out[7] vdd gnd INVERTER -XINVERTER_[8] Z[8] decode_out[8] vdd gnd INVERTER -XINVERTER_[9] Z[9] decode_out[9] vdd gnd INVERTER -XINVERTER_[10] Z[10] decode_out[10] vdd gnd INVERTER -XINVERTER_[11] Z[11] decode_out[11] vdd gnd INVERTER -XINVERTER_[12] Z[12] decode_out[12] vdd gnd INVERTER -XINVERTER_[13] Z[13] decode_out[13] vdd gnd INVERTER -XINVERTER_[14] Z[14] decode_out[14] vdd gnd INVERTER -XINVERTER_[15] Z[15] decode_out[15] vdd gnd INVERTER -.ENDS hierarchical_decoder - -.SUBCKT msf_address ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] addr_clk vdd gnd -XXdff0 ADDR[0] A[0] A_bar[0] addr_clk vdd gnd ms_flop -XXdff1 ADDR[1] A[1] A_bar[1] addr_clk vdd gnd ms_flop -XXdff2 ADDR[2] A[2] A_bar[2] addr_clk vdd gnd ms_flop -XXdff3 ADDR[3] A[3] A_bar[3] addr_clk vdd gnd ms_flop -.ENDS msf_address - -.SUBCKT msf_data_in DATA[0] DATA[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk vdd gnd -XXdff0 DATA[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop -XXdff1 DATA[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop -.ENDS msf_data_in - -.SUBCKT msf_data_out data_out[0] data_out[1] tri_in[0] tri_in_bar[0] tri_in[1] tri_in_bar[1] sclk vdd gnd -XXdff0 data_out[0] tri_in[0] tri_in_bar[0] sclk vdd gnd ms_flop -XXdff1 data_out[1] tri_in[1] tri_in_bar[1] sclk vdd gnd ms_flop -.ENDS msf_data_out -*********************** tri_gate ****************************** - -.SUBCKT tri_gate in out en en_bar vdd gnd - -M_1 net_2 in_inv gnd gnd n W='1.2*1u' L=0.6u -M_2 net_3 in_inv vdd vdd p W='2.4*1u' L=0.6u -M_3 out en_bar net_3 vdd p W='2.4*1u' L=0.6u -M_4 out en net_2 gnd n W='1.2*1u' L=0.6u -M_5 in_inv in vdd vdd p W='2.4*1u' L=0.6u -M_6 in_inv in gnd gnd n W='1.2*1u' L=0.6u - - -.ENDS - -.SUBCKT tri_gate_array tri_in[0] tri_in[1] DATA[0] DATA[1] en en_bar vdd gnd -XXtri_gate0 tri_in[0] DATA[0] en en_bar vdd gnd tri_gate -XXtri_gate1 tri_in[1] DATA[1] en en_bar vdd gnd tri_gate -.ENDS tri_gate_array - -.SUBCKT wordline_driver decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] clk vdd gnd -XWordline_driver_inv_clk0 clk clk_bar[0] vdd gnd INVERTER -XWordline_driver_nand0 decode_out[0] clk_bar[0] net[0] vdd gnd NAND2 -XWordline_driver_inv0 net[0] wl[0] vdd gnd INVERTER -XWordline_driver_inv_clk1 clk clk_bar[1] vdd gnd INVERTER -XWordline_driver_nand1 decode_out[1] clk_bar[1] net[1] vdd gnd NAND2 -XWordline_driver_inv1 net[1] wl[1] vdd gnd INVERTER -XWordline_driver_inv_clk2 clk clk_bar[2] vdd gnd INVERTER -XWordline_driver_nand2 decode_out[2] clk_bar[2] net[2] vdd gnd NAND2 -XWordline_driver_inv2 net[2] wl[2] vdd gnd INVERTER -XWordline_driver_inv_clk3 clk clk_bar[3] vdd gnd INVERTER -XWordline_driver_nand3 decode_out[3] clk_bar[3] net[3] vdd gnd NAND2 -XWordline_driver_inv3 net[3] wl[3] vdd gnd INVERTER -XWordline_driver_inv_clk4 clk clk_bar[4] vdd gnd INVERTER -XWordline_driver_nand4 decode_out[4] clk_bar[4] net[4] vdd gnd NAND2 -XWordline_driver_inv4 net[4] wl[4] vdd gnd INVERTER -XWordline_driver_inv_clk5 clk clk_bar[5] vdd gnd INVERTER -XWordline_driver_nand5 decode_out[5] clk_bar[5] net[5] vdd gnd NAND2 -XWordline_driver_inv5 net[5] wl[5] vdd gnd INVERTER -XWordline_driver_inv_clk6 clk clk_bar[6] vdd gnd INVERTER -XWordline_driver_nand6 decode_out[6] clk_bar[6] net[6] vdd gnd NAND2 -XWordline_driver_inv6 net[6] wl[6] vdd gnd INVERTER -XWordline_driver_inv_clk7 clk clk_bar[7] vdd gnd INVERTER -XWordline_driver_nand7 decode_out[7] clk_bar[7] net[7] vdd gnd NAND2 -XWordline_driver_inv7 net[7] wl[7] vdd gnd INVERTER -XWordline_driver_inv_clk8 clk clk_bar[8] vdd gnd INVERTER -XWordline_driver_nand8 decode_out[8] clk_bar[8] net[8] vdd gnd NAND2 -XWordline_driver_inv8 net[8] wl[8] vdd gnd INVERTER -XWordline_driver_inv_clk9 clk clk_bar[9] vdd gnd INVERTER -XWordline_driver_nand9 decode_out[9] clk_bar[9] net[9] vdd gnd NAND2 -XWordline_driver_inv9 net[9] wl[9] vdd gnd INVERTER -XWordline_driver_inv_clk10 clk clk_bar[10] vdd gnd INVERTER -XWordline_driver_nand10 decode_out[10] clk_bar[10] net[10] vdd gnd NAND2 -XWordline_driver_inv10 net[10] wl[10] vdd gnd INVERTER -XWordline_driver_inv_clk11 clk clk_bar[11] vdd gnd INVERTER -XWordline_driver_nand11 decode_out[11] clk_bar[11] net[11] vdd gnd NAND2 -XWordline_driver_inv11 net[11] wl[11] vdd gnd INVERTER -XWordline_driver_inv_clk12 clk clk_bar[12] vdd gnd INVERTER -XWordline_driver_nand12 decode_out[12] clk_bar[12] net[12] vdd gnd NAND2 -XWordline_driver_inv12 net[12] wl[12] vdd gnd INVERTER -XWordline_driver_inv_clk13 clk clk_bar[13] vdd gnd INVERTER -XWordline_driver_nand13 decode_out[13] clk_bar[13] net[13] vdd gnd NAND2 -XWordline_driver_inv13 net[13] wl[13] vdd gnd INVERTER -XWordline_driver_inv_clk14 clk clk_bar[14] vdd gnd INVERTER -XWordline_driver_nand14 decode_out[14] clk_bar[14] net[14] vdd gnd NAND2 -XWordline_driver_inv14 net[14] wl[14] vdd gnd INVERTER -XWordline_driver_inv_clk15 clk clk_bar[15] vdd gnd INVERTER -XWordline_driver_nand15 decode_out[15] clk_bar[15] net[15] vdd gnd NAND2 -XWordline_driver_inv15 net[15] wl[15] vdd gnd INVERTER -.ENDS wordline_driver - -.SUBCKT inv_nmos181 D G S B -Mnmos D G S B n m=4 w=1.2u l=0.6u -.ENDS inv_nmos181 - -.SUBCKT inv_pmos182 D G S B -Mpmos D G S B p m=4 w=2.4u l=0.6u -.ENDS inv_pmos182 - -.SUBCKT pinv4x A Z vdd gnd -Xpinv_nmos Z A gnd gnd inv_nmos181 -Xpinv_pmos Z A vdd vdd inv_pmos182 -.ENDS pinv4x - -.SUBCKT nor_2_nmos195 D G S B -Mnmos D G S B n m=1 w=1.2u l=0.6u -.ENDS nor_2_nmos195 - -.SUBCKT nor_2_nmos296 D G S B -Mnmos D G S B n m=1 w=1.2u l=0.6u -.ENDS nor_2_nmos296 - -.SUBCKT nor_2_pmos197 D G S B -Mpmos D G S B p m=4 w=1.2u l=0.6u -.ENDS nor_2_pmos197 - -.SUBCKT nor_2_pmos298 D G S B -Mpmos D G S B p m=4 w=1.2u l=0.6u -.ENDS nor_2_pmos298 - -.SUBCKT NOR2 A B Z vdd gnd -Xnmos1 Z A gnd gnd nor_2_nmos195 -Xnmos2 Z B gnd gnd nor_2_nmos296 -Xpmos1 vdd A net1 vdd nor_2_pmos197 -Xpmos2 net1 B Z vdd nor_2_pmos298 -.ENDS NOR2 - -.SUBCKT test_bank1 DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd -Xbitcell_array bl[0] br[0] bl[1] br[1] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] vdd gnd bitcell_array -Xprecharge_array bl[0] br[0] bl[1] br[1] clk_bar vdd precharge_array -Xsense_amp_array bl[0] br[0] bl[1] br[1] data_out[0] data_out[1] s_en vdd gnd sense_amp_array -Xwrite_driver_array data_in[0] data_in[1] bl[0] br[0] bl[1] br[1] w_en vdd gnd write_driver_array -Xdata_in_flop_array DATA[0] DATA[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk_bar vdd gnd msf_data_in -Xtrigate_data_array data_out[0] data_out[1] DATA[0] DATA[1] tri_en tri_en_bar vdd gnd tri_gate_array -Xaddress_decoder A[0] A[1] A[2] A[3] decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] vdd gnd hierarchical_decoder -Xwordline_driver decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] clk vdd gnd wordline_driver -Xaddress_flop_array ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] clk vdd gnd msf_address -.ENDS test_bank1 - -.SUBCKT testsram DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb OEb clk vdd gnd -Xbank0 DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd test_bank1 -Xcontrol CSb WEb OEb s_en w_en tri_en tri_en_bar clk_bar clk vdd gnd control_logic -.ENDS testsram diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm.v b/compiler/tests/golden/sram_2_16_1_scn3me_subm.v deleted file mode 100644 index fc2b6cd0..00000000 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm.v +++ /dev/null @@ -1,47 +0,0 @@ -// OpenRAM SRAM model -// Words: 16 -// Word size: 2 - -module sram_2_16_1_scn3me_subm(DATA,ADDR,CSb,WEb,OEb,clk); - - parameter DATA_WIDTH = 2 ; - parameter ADDR_WIDTH = 4 ; - parameter RAM_DEPTH = 1 << ADDR_WIDTH; - parameter DELAY = 3 ; - - inout [DATA_WIDTH-1:0] DATA; - input [ADDR_WIDTH-1:0] ADDR; - input CSb; // active low chip select - input WEb; // active low write control - input OEb; // active output enable - input clk; // clock - - reg [DATA_WIDTH-1:0] data_out ; - reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; - - // Tri-State Buffer control - // output : When WEb = 1, oeb = 0, csb = 0 - assign DATA = (!CSb && !OEb && WEb) ? data_out : 2'bz; - - // Memory Write Block - // Write Operation : When WEb = 0, CSb = 0 - always @ (posedge clk) - begin : MEM_WRITE - if ( !CSb && !WEb ) begin - mem[ADDR] = DATA; - $display($time," Writing %m ABUS=%b DATA=%b",ADDR,DATA); - end - end - - - // Memory Read Block - // Read Operation : When WEb = 1, CSb = 0 - always @ (posedge clk) - begin : MEM_READ - if (!CSb && WEb) begin - data_out <= #(DELAY) mem[ADDR]; - $display($time," Reading %m ABUS=%b DATA=%b",ADDR,mem[ADDR]); - end - end - -endmodule 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 deleted file mode 100644 index e6aa54f9..00000000 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C.lib +++ /dev/null @@ -1,318 +0,0 @@ -library (sram_2_16_1_scn3me_subm_TT_5p0V_25C_lib){ - delay_model : "table_lookup"; - time_unit : "1ns" ; - voltage_unit : "1v" ; - current_unit : "1mA" ; - resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; - leakage_power_unit : "1mW" ; - pulling_resistance_unit :"1kohm" ; - operating_conditions(OC){ - process : 1.0 ; - voltage : 5.0 ; - temperature : 25; - } - - input_threshold_pct_fall : 50.0 ; - output_threshold_pct_fall : 50.0 ; - input_threshold_pct_rise : 50.0 ; - output_threshold_pct_rise : 50.0 ; - slew_lower_threshold_pct_fall : 10.0 ; - slew_upper_threshold_pct_fall : 90.0 ; - slew_lower_threshold_pct_rise : 10.0 ; - slew_upper_threshold_pct_rise : 90.0 ; - - nom_voltage : 5.0; - nom_temperature : 25; - nom_process : 1.0; - default_cell_leakage_power : 0.0 ; - default_leakage_power_density : 0.0 ; - default_input_pin_cap : 1.0 ; - default_inout_pin_cap : 1.0 ; - default_output_pin_cap : 0.0 ; - default_max_transition : 0.5 ; - default_fanout_load : 1.0 ; - default_max_fanout : 4.0 ; - default_connection_class : universal ; - - lu_table_template(CELL_TABLE){ - variable_1 : input_net_transition; - variable_2 : total_output_net_capacitance; - index_1("0.0125, 0.05, 0.4"); - index_2("2.45605, 9.8242, 78.5936"); - } - - lu_table_template(CONSTRAINT_TABLE){ - variable_1 : related_pin_transition; - variable_2 : constrained_pin_transition; - index_1("0.0125, 0.05, 0.4"); - index_2("0.0125, 0.05, 0.4"); - } - - default_operating_conditions : OC; - - - type (DATA){ - base_type : array; - data_type : bit; - bit_width : 2; - bit_from : 0; - bit_to : 1; - } - - type (ADDR){ - base_type : array; - data_type : bit; - bit_width : 4; - bit_from : 0; - bit_to : 3; - } - -cell (sram_2_16_1_scn3me_subm){ - memory(){ - type : ram; - address_width : 4; - word_width : 2; - } - interface_timing : true; - dont_use : true; - map_only : true; - dont_touch : true; - area : 142800.38999999998; - - leakage_power () { - when : "CSb"; - value : 0.0252988; - } - cell_leakage_power : 0; - bus(DIN){ - bus_type : DATA; - direction : input; - capacitance : 9.8242; - memory_write(){ - address : ADDR; - clocked_on : clk; - } - } - bus(DOUT){ - bus_type : DATA; - direction : output; - max_capacitance : 78.5936; - min_capacitance : 2.45605; - memory_read(){ - address : ADDR; - } - pin(DOUT[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132"); - } - } - timing(){ - timing_sense : non_unate; - related_pin : "clk"; - timing_type : rising_edge; - cell_rise(CELL_TABLE) { - values("0.945, 0.976, 1.139",\ - "0.948, 0.98, 1.143",\ - "1.003, 1.036, 1.202"); - } - cell_fall(CELL_TABLE) { - 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.605, 0.629, 0.98",\ - "0.605, 0.629, 0.979",\ - "0.604, 0.628, 0.973"); - } - fall_transition(CELL_TABLE) { - values("11.17, 11.175, 1.284",\ - "11.167, 11.173, 1.284",\ - "11.173, 11.179, 11.473"); - } - } - } - } - - bus(ADDR){ - bus_type : ADDR; - direction : input; - capacitance : 9.8242; - max_transition : 0.4; - pin(ADDR[3:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132"); - } - } - } - } - - pin(CSb){ - direction : input; - capacitance : 9.8242; - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132"); - } - } - } - - pin(WEb){ - direction : input; - capacitance : 9.8242; - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132"); - } - } - } - - pin(clk){ - clock : true; - direction : input; - capacitance : 9.8242; - internal_power(){ - when : "!CSb & clk & !WEb"; - rise_power(scalar){ - values("2.1762222222222225"); - } - fall_power(scalar){ - values("2.1762222222222225"); - } - } - internal_power(){ - when : "!CSb & !clk & WEb"; - rise_power(scalar){ - values("2.167955555555556"); - } - fall_power(scalar){ - values("2.167955555555556"); - } - } - internal_power(){ - when : "CSb"; - rise_power(scalar){ - values("0"); - } - fall_power(scalar){ - values("0"); - } - } - timing(){ - timing_type :"min_pulse_width"; - related_pin : clk; - rise_constraint(scalar) { - values("9.6875"); - } - fall_constraint(scalar) { - values("9.6875"); - } - } - timing(){ - timing_type :"minimum_period"; - related_pin : clk; - rise_constraint(scalar) { - values("19.375"); - } - fall_constraint(scalar) { - 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 deleted file mode 100644 index 8d774ce5..00000000 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_analytical.lib +++ /dev/null @@ -1,318 +0,0 @@ -library (sram_2_16_1_scn3me_subm_TT_5p0V_25C_lib){ - delay_model : "table_lookup"; - time_unit : "1ns" ; - voltage_unit : "1v" ; - current_unit : "1mA" ; - resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; - leakage_power_unit : "1mW" ; - pulling_resistance_unit :"1kohm" ; - operating_conditions(OC){ - process : 1.0 ; - voltage : 5.0 ; - temperature : 25; - } - - input_threshold_pct_fall : 50.0 ; - output_threshold_pct_fall : 50.0 ; - input_threshold_pct_rise : 50.0 ; - output_threshold_pct_rise : 50.0 ; - slew_lower_threshold_pct_fall : 10.0 ; - slew_upper_threshold_pct_fall : 90.0 ; - slew_lower_threshold_pct_rise : 10.0 ; - slew_upper_threshold_pct_rise : 90.0 ; - - nom_voltage : 5.0; - nom_temperature : 25; - nom_process : 1.0; - default_cell_leakage_power : 0.0 ; - default_leakage_power_density : 0.0 ; - default_input_pin_cap : 1.0 ; - default_inout_pin_cap : 1.0 ; - default_output_pin_cap : 0.0 ; - default_max_transition : 0.5 ; - default_fanout_load : 1.0 ; - default_max_fanout : 4.0 ; - default_connection_class : universal ; - - lu_table_template(CELL_TABLE){ - variable_1 : input_net_transition; - variable_2 : total_output_net_capacitance; - index_1("0.0125, 0.05, 0.4"); - index_2("2.45605, 9.8242, 78.5936"); - } - - lu_table_template(CONSTRAINT_TABLE){ - variable_1 : related_pin_transition; - variable_2 : constrained_pin_transition; - index_1("0.0125, 0.05, 0.4"); - index_2("0.0125, 0.05, 0.4"); - } - - default_operating_conditions : OC; - - - type (DATA){ - base_type : array; - data_type : bit; - bit_width : 2; - bit_from : 0; - bit_to : 1; - } - - type (ADDR){ - base_type : array; - data_type : bit; - bit_width : 4; - bit_from : 0; - bit_to : 3; - } - -cell (sram_2_16_1_scn3me_subm){ - memory(){ - type : ram; - address_width : 4; - word_width : 2; - } - interface_timing : true; - dont_use : true; - map_only : true; - dont_touch : true; - area : 142800.38999999998; - - leakage_power () { - when : "CSb"; - value : 0.000168; - } - cell_leakage_power : 0; - bus(DIN){ - bus_type : DATA; - direction : input; - capacitance : 9.8242; - memory_write(){ - address : ADDR0; - clocked_on : clk; - } - } - bus(DOUT){ - bus_type : DATA; - direction : output; - max_capacitance : 78.5936; - min_capacitance : 2.45605; - memory_read(){ - address : ADDR0; - } - pin(DOUT[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - timing(){ - timing_sense : non_unate; - related_pin : "clk"; - timing_type : rising_edge; - cell_rise(CELL_TABLE) { - 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.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",\ - "0.024, 0.081, 0.61",\ - "0.024, 0.081, 0.61"); - } - fall_transition(CELL_TABLE) { - values("0.024, 0.081, 0.61",\ - "0.024, 0.081, 0.61",\ - "0.024, 0.081, 0.61"); - } - } - } - } - - bus(ADDR){ - bus_type : ADDR; - direction : input; - capacitance : 9.8242; - max_transition : 0.4; - pin(ADDR[3:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - } - } - - pin(CSb){ - direction : input; - capacitance : 9.8242; - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - } - - pin(WEb){ - direction : input; - capacitance : 9.8242; - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - } - - pin(clk){ - clock : true; - direction : input; - capacitance : 9.8242; - internal_power(){ - when : "!CSb & clk & !WEb"; - rise_power(scalar){ - values("10.559086132533329"); - } - fall_power(scalar){ - values("10.559086132533329"); - } - } - internal_power(){ - when : "!CSb & !clk & WEb"; - rise_power(scalar){ - values("10.559086132533329"); - } - fall_power(scalar){ - values("10.559086132533329"); - } - } - internal_power(){ - when : "CSb"; - rise_power(scalar){ - values("0"); - } - fall_power(scalar){ - values("0"); - } - } - timing(){ - timing_type :"min_pulse_width"; - related_pin : clk; - rise_constraint(scalar) { - values("0.0"); - } - fall_constraint(scalar) { - values("0.0"); - } - } - timing(){ - timing_type :"minimum_period"; - related_pin : clk; - rise_constraint(scalar) { - values("0"); - } - fall_constraint(scalar) { - values("0"); - } - } - } - } -} diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_pruned.lib b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_pruned.lib deleted file mode 100644 index b514a858..00000000 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_pruned.lib +++ /dev/null @@ -1,318 +0,0 @@ -library (sram_2_16_1_scn3me_subm_TT_5p0V_25C_lib){ - delay_model : "table_lookup"; - time_unit : "1ns" ; - voltage_unit : "1v" ; - current_unit : "1mA" ; - resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; - leakage_power_unit : "1mW" ; - pulling_resistance_unit :"1kohm" ; - operating_conditions(OC){ - process : 1.0 ; - voltage : 5.0 ; - temperature : 25; - } - - input_threshold_pct_fall : 50.0 ; - output_threshold_pct_fall : 50.0 ; - input_threshold_pct_rise : 50.0 ; - output_threshold_pct_rise : 50.0 ; - slew_lower_threshold_pct_fall : 10.0 ; - slew_upper_threshold_pct_fall : 90.0 ; - slew_lower_threshold_pct_rise : 10.0 ; - slew_upper_threshold_pct_rise : 90.0 ; - - nom_voltage : 5.0; - nom_temperature : 25; - nom_process : 1.0; - default_cell_leakage_power : 0.0 ; - default_leakage_power_density : 0.0 ; - default_input_pin_cap : 1.0 ; - default_inout_pin_cap : 1.0 ; - default_output_pin_cap : 0.0 ; - default_max_transition : 0.5 ; - default_fanout_load : 1.0 ; - default_max_fanout : 4.0 ; - default_connection_class : universal ; - - lu_table_template(CELL_TABLE){ - variable_1 : input_net_transition; - variable_2 : total_output_net_capacitance; - index_1("0.0125, 0.05, 0.4"); - index_2("2.45605, 9.8242, 78.5936"); - } - - lu_table_template(CONSTRAINT_TABLE){ - variable_1 : related_pin_transition; - variable_2 : constrained_pin_transition; - index_1("0.0125, 0.05, 0.4"); - index_2("0.0125, 0.05, 0.4"); - } - - default_operating_conditions : OC; - - - type (DATA){ - base_type : array; - data_type : bit; - bit_width : 2; - bit_from : 0; - bit_to : 1; - } - - type (ADDR){ - base_type : array; - data_type : bit; - bit_width : 4; - bit_from : 0; - bit_to : 3; - } - -cell (sram_2_16_1_scn3me_subm){ - memory(){ - type : ram; - address_width : 4; - word_width : 2; - } - interface_timing : true; - dont_use : true; - map_only : true; - dont_touch : true; - area : 142800.38999999998; - - leakage_power () { - when : "CSb"; - value : 0.0252988; - } - cell_leakage_power : 0; - bus(DIN){ - bus_type : DATA; - direction : input; - capacitance : 9.8242; - memory_write(){ - address : ADDR; - clocked_on : clk; - } - } - bus(DOUT){ - bus_type : DATA; - direction : output; - max_capacitance : 78.5936; - min_capacitance : 2.45605; - memory_read(){ - address : ADDR; - } - pin(DOUT[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132"); - } - } - timing(){ - timing_sense : non_unate; - related_pin : "clk"; - timing_type : rising_edge; - cell_rise(CELL_TABLE) { - values("0.928, 0.959, 1.113",\ - "0.931, 0.962, 1.116",\ - "0.985, 1.018, 1.176"); - } - cell_fall(CELL_TABLE) { - 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.599, 0.624, 0.968",\ - "0.599, 0.623, 0.97",\ - "0.598, 0.623, 0.967"); - } - fall_transition(CELL_TABLE) { - values("11.157, 11.165, 11.446",\ - "11.159, 11.162, 1.271",\ - "11.158, 11.165, 11.47"); - } - } - } - } - - bus(ADDR){ - bus_type : ADDR; - direction : input; - capacitance : 9.8242; - max_transition : 0.4; - pin(ADDR[3:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132"); - } - } - } - } - - pin(CSb){ - direction : input; - capacitance : 9.8242; - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132"); - } - } - } - - pin(WEb){ - direction : input; - capacitance : 9.8242; - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149",\ - "0.076, 0.076, 0.149"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027",\ - "0.033, 0.039, 0.027"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009",\ - "-0.004, -0.004, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132",\ - "-0.052, -0.059, -0.132"); - } - } - } - - pin(clk){ - clock : true; - direction : input; - capacitance : 9.8242; - internal_power(){ - when : "!CSb & clk & !WEb"; - rise_power(scalar){ - values("2.033783461111111"); - } - fall_power(scalar){ - values("2.033783461111111"); - } - } - internal_power(){ - when : "!CSb & !clk & WEb"; - rise_power(scalar){ - values("2.032705683333334"); - } - fall_power(scalar){ - values("2.032705683333334"); - } - } - internal_power(){ - when : "CSb"; - rise_power(scalar){ - values("0"); - } - fall_power(scalar){ - values("0"); - } - } - timing(){ - timing_type :"min_pulse_width"; - related_pin : clk; - rise_constraint(scalar) { - values("9.6875"); - } - fall_constraint(scalar) { - values("9.6875"); - } - } - timing(){ - timing_type :"minimum_period"; - related_pin : clk; - rise_constraint(scalar) { - values("19.375"); - } - fall_constraint(scalar) { - values("19.375"); - } - } - } - } -} 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 de4c077c..7017b8a7 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm.v +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm.v @@ -2,46 +2,60 @@ // Words: 16 // Word size: 2 -module sram_2_16_1_scn4m_subm(DATA,ADDR,CSb,WEb,OEb,clk); +module sram_2_16_1_scn4m_subm( +// Port 0: RW + clk0,csb0,web0,ADDR0,DIN0,DOUT0 + ); parameter DATA_WIDTH = 2 ; parameter ADDR_WIDTH = 4 ; parameter RAM_DEPTH = 1 << ADDR_WIDTH; + // FIXME: This delay is arbitrary. parameter DELAY = 3 ; - inout [DATA_WIDTH-1:0] DATA; - input [ADDR_WIDTH-1:0] ADDR; - input CSb; // active low chip select - input WEb; // active low write control - input OEb; // active output enable - input clk; // clock + input clk0; // clock + input csb0; // active low chip select + input web0; // active low write control + input [ADDR_WIDTH-1:0] ADDR0; + input [DATA_WIDTH-1:0] DIN0; + output [DATA_WIDTH-1:0] DOUT0; - reg [DATA_WIDTH-1:0] data_out ; - reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; + reg csb0_reg; + reg web0_reg; + reg [ADDR_WIDTH-1:0] ADDR0_reg; + reg [DATA_WIDTH-1:0] DIN0_reg; + reg [DATA_WIDTH-1:0] DOUT0; - // Tri-State Buffer control - // output : When WEb = 1, oeb = 0, csb = 0 - assign DATA = (!CSb && !OEb && WEb) ? data_out : 2'bz; - - // Memory Write Block - // Write Operation : When WEb = 0, CSb = 0 - always @ (posedge clk) - begin : MEM_WRITE - if ( !CSb && !WEb ) begin - mem[ADDR] = DATA; - $display($time," Writing %m ABUS=%b DATA=%b",ADDR,DATA); - end + // All inputs are registers + always @(posedge clk0) + begin + csb0_reg = csb0; + web0_reg = web0; + ADDR0_reg = ADDR0; + DIN0_reg = DIN0; + DOUT0 = 2'bx; + if ( !csb0_reg && web0_reg ) + $display($time," Reading %m ADDR0=%b DOUT0=%b",ADDR0_reg,mem[ADDR0_reg]); + if ( !csb0_reg && !web0_reg ) + $display($time," Writing %m ADDR0=%b DIN0=%b",ADDR0_reg,DIN0_reg); end +reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; - // Memory Read Block - // Read Operation : When WEb = 1, CSb = 0 - always @ (posedge clk) - begin : MEM_READ - if (!CSb && WEb) begin - data_out <= #(DELAY) mem[ADDR]; - $display($time," Reading %m ABUS=%b DATA=%b",ADDR,mem[ADDR]); - end + // Memory Write Block Port 0 + // Write Operation : When web0 = 0, csb0 = 0 + always @ (negedge clk0) + begin : MEM_WRITE0 + if ( !csb0_reg && !web0_reg ) + mem[ADDR0_reg] = DIN0_reg; + end + + // Memory Read Block Port 0 + // Read Operation : When web0 = 1, csb0 = 0 + always @ (negedge clk0) + begin : MEM_READ0 + if (!csb0_reg && web0_reg) + DOUT0 <= #(DELAY) mem[ADDR0_reg]; end endmodule diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib index 89f40320..d6fc30aa 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib @@ -78,11 +78,11 @@ cell (sram_2_16_1_scn4m_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 60176.520000000004; + area : 60774.3; leakage_power () { when : "CSb0"; - value : 0.000175; + value : 0.0009813788999999999; } cell_leakage_power : 0; bus(DIN0){ @@ -91,7 +91,37 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; memory_write(){ address : ADDR0; - clocked_on : clk; + clocked_on : clk0; + } + pin(DIN0){ + timing(){ + timing_type : setup_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); + } + } } } bus(DOUT0){ @@ -102,58 +132,30 @@ cell (sram_2_16_1_scn4m_subm){ memory_read(){ address : ADDR0; } - pin(DOUT0[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } + pin(DOUT0){ timing(){ timing_sense : non_unate; - related_pin : "clk"; - timing_type : rising_edge; + related_pin : "clk0"; + timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("0.268, 0.268, 0.268",\ - "0.268, 0.268, 0.268",\ - "0.268, 0.268, 0.268"); + values("1.556, 1.576, 1.751",\ + "1.559, 1.579, 1.754",\ + "1.624, 1.643, 1.819"); } cell_fall(CELL_TABLE) { - values("0.268, 0.268, 0.268",\ - "0.268, 0.268, 0.268",\ - "0.268, 0.268, 0.268"); + values("3.445, 3.504, 3.926",\ + "3.448, 3.507, 3.93",\ + "3.49, 3.549, 3.972"); } rise_transition(CELL_TABLE) { - values("0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004"); + values("0.13, 0.169, 0.574",\ + "0.13, 0.169, 0.574",\ + "0.13, 0.169, 0.574"); } fall_transition(CELL_TABLE) { - values("0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004"); + values("0.467, 0.49, 0.959",\ + "0.467, 0.49, 0.959",\ + "0.47, 0.493, 0.96"); } } } @@ -164,33 +166,33 @@ cell (sram_2_16_1_scn4m_subm){ direction : input; capacitance : 9.8242; max_transition : 0.4; - pin(ADDR0[3:0]){ + pin(ADDR0){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); + values("-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); } } } @@ -201,30 +203,30 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); + values("-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); } } } @@ -234,54 +236,54 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); + values("-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); } } } - pin(clk){ + pin(clk0){ clock : true; direction : input; capacitance : 9.8242; internal_power(){ - when : "!CSb0 & clk & !WEb0"; + when : "!CSb0 & clk0 & !WEb0"; rise_power(scalar){ - values("11.3007276371"); + values("9.972790277777777"); } fall_power(scalar){ - values("11.3007276371"); + values("9.972790277777777"); } } internal_power(){ - when : "!CSb0 & !clk & WEb0"; + when : "!CSb0 & !clk0 & WEb0"; rise_power(scalar){ - values("11.3007276371"); + values("8.899322499999998"); } fall_power(scalar){ - values("11.3007276371"); + values("8.899322499999998"); } } internal_power(){ @@ -295,24 +297,25 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type :"min_pulse_width"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { - values("0.0"); + values("2.344"); } fall_constraint(scalar) { - values("0.0"); + values("2.344"); } } timing(){ timing_type :"minimum_period"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { - values("0"); + values("4.688"); } fall_constraint(scalar) { - values("0"); + values("4.688"); } } } + } } diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_analytical.lib index 89f40320..5c56ab1e 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_analytical.lib @@ -78,11 +78,11 @@ cell (sram_2_16_1_scn4m_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 60176.520000000004; + area : 60774.3; leakage_power () { when : "CSb0"; - value : 0.000175; + value : 0.000179; } cell_leakage_power : 0; bus(DIN0){ @@ -91,21 +91,12 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; memory_write(){ address : ADDR0; - clocked_on : clk; + clocked_on : clk0; } - } - bus(DOUT0){ - bus_type : DATA; - direction : output; - max_capacitance : 78.5936; - min_capacitance : 2.45605; - memory_read(){ - address : ADDR0; - } - pin(DOUT0[1:0]){ + pin(DIN0){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -119,7 +110,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -131,10 +122,21 @@ cell (sram_2_16_1_scn4m_subm){ "0.001, 0.001, 0.001"); } } + } + } + bus(DOUT0){ + bus_type : DATA; + direction : output; + max_capacitance : 78.5936; + min_capacitance : 2.45605; + memory_read(){ + address : ADDR0; + } + pin(DOUT0){ timing(){ timing_sense : non_unate; - related_pin : "clk"; - timing_type : rising_edge; + related_pin : "clk0"; + timing_type : falling_edge; cell_rise(CELL_TABLE) { values("0.268, 0.268, 0.268",\ "0.268, 0.268, 0.268",\ @@ -164,10 +166,10 @@ cell (sram_2_16_1_scn4m_subm){ direction : input; capacitance : 9.8242; max_transition : 0.4; - pin(ADDR0[3:0]){ + pin(ADDR0){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -181,7 +183,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -201,7 +203,7 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -215,7 +217,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -234,7 +236,7 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -248,7 +250,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -262,26 +264,26 @@ cell (sram_2_16_1_scn4m_subm){ } } - pin(clk){ + pin(clk0){ clock : true; direction : input; capacitance : 9.8242; internal_power(){ - when : "!CSb0 & clk & !WEb0"; + when : "!CSb0 & clk0 & !WEb0"; rise_power(scalar){ - values("11.3007276371"); + values("11.3049604371"); } fall_power(scalar){ - values("11.3007276371"); + values("11.3049604371"); } } internal_power(){ - when : "!CSb0 & !clk & WEb0"; + when : "!CSb0 & !clk0 & WEb0"; rise_power(scalar){ - values("11.3007276371"); + values("11.3049604371"); } fall_power(scalar){ - values("11.3007276371"); + values("11.3049604371"); } } internal_power(){ @@ -295,7 +297,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type :"min_pulse_width"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("0.0"); } @@ -305,7 +307,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type :"minimum_period"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("0"); } @@ -314,5 +316,6 @@ cell (sram_2_16_1_scn4m_subm){ } } } + } } diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_pruned.lib b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_pruned.lib index 8509fc30..64340a5b 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_pruned.lib +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_pruned.lib @@ -78,11 +78,11 @@ cell (sram_2_16_1_scn4m_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 60176.520000000004; + area : 60774.3; leakage_power () { when : "CSb0"; - value : 0.025716199999999998; + value : 0.0009813788999999999; } cell_leakage_power : 0; bus(DIN0){ @@ -91,7 +91,37 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; memory_write(){ address : ADDR0; - clocked_on : clk; + clocked_on : clk0; + } + pin(DIN0){ + timing(){ + timing_type : setup_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); + } + } } } bus(DOUT0){ @@ -102,58 +132,30 @@ cell (sram_2_16_1_scn4m_subm){ memory_read(){ address : ADDR0; } - pin(DOUT0[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("-0.065, -0.071, -0.114",\ - "-0.065, -0.071, -0.114",\ - "-0.065, -0.071, -0.114"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095"); - } - } + pin(DOUT0){ timing(){ timing_sense : non_unate; - related_pin : "clk"; - timing_type : rising_edge; + related_pin : "clk0"; + timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("1.277, 1.297, 1.475",\ - "1.28, 1.3, 1.479",\ - "1.347, 1.367, 1.545"); + values("1.542, 1.562, 1.738",\ + "1.545, 1.565, 1.741",\ + "1.609, 1.629, 1.805"); } cell_fall(CELL_TABLE) { - values("3.217, 3.281, 3.71",\ - "3.22, 3.285, 3.714",\ - "3.261, 3.325, 3.75"); + values("3.446, 3.505, 3.924",\ + "3.45, 3.508, 3.927",\ + "3.491, 3.55, 3.97"); } rise_transition(CELL_TABLE) { - values("0.122, 0.164, 0.579",\ - "0.122, 0.164, 0.578",\ - "0.122, 0.164, 0.58"); + values("0.129, 0.169, 0.573",\ + "0.129, 0.169, 0.573",\ + "0.129, 0.169, 0.573"); } fall_transition(CELL_TABLE) { - values("0.363, 0.396, 0.958",\ - "0.363, 0.396, 0.957",\ - "0.366, 0.399, 0.951"); + values("0.457, 0.481, 0.956",\ + "0.457, 0.481, 0.956",\ + "0.459, 0.483, 0.957"); } } } @@ -164,33 +166,33 @@ cell (sram_2_16_1_scn4m_subm){ direction : input; capacitance : 9.8242; max_transition : 0.4; - pin(ADDR0[3:0]){ + pin(ADDR0){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228"); + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143"); + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("-0.065, -0.071, -0.114",\ "-0.065, -0.071, -0.114",\ "-0.065, -0.071, -0.114"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095"); + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); } } } @@ -201,30 +203,30 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228"); + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143"); + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("-0.065, -0.071, -0.114",\ "-0.065, -0.071, -0.114",\ "-0.065, -0.071, -0.114"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095"); + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); } } } @@ -234,54 +236,54 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228"); + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143"); + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("-0.065, -0.071, -0.114",\ "-0.065, -0.071, -0.114",\ "-0.065, -0.071, -0.114"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095"); + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); } } } - pin(clk){ + pin(clk0){ clock : true; direction : input; capacitance : 9.8242; internal_power(){ - when : "!CSb0 & clk & !WEb0"; + when : "!CSb0 & clk0 & !WEb0"; rise_power(scalar){ - values("9.141838916666668"); + values("9.602821763527778"); } fall_power(scalar){ - values("9.141838916666668"); + values("9.602821763527778"); } } internal_power(){ - when : "!CSb0 & !clk & WEb0"; + when : "!CSb0 & !clk0 & WEb0"; rise_power(scalar){ - values("8.304491694444444"); + values("8.647938152416664"); } fall_power(scalar){ - values("8.304491694444444"); + values("8.647938152416664"); } } internal_power(){ @@ -295,7 +297,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type :"min_pulse_width"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("2.344"); } @@ -305,7 +307,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type :"minimum_period"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("4.688"); } @@ -314,5 +316,6 @@ cell (sram_2_16_1_scn4m_subm){ } } } + } } diff --git a/compiler/tests/out.log b/compiler/tests/out.log deleted file mode 100644 index e69de29b..00000000 diff --git a/compiler/tests/sram1.gds b/compiler/tests/sram1.gds deleted file mode 100644 index d6ed28eb..00000000 Binary files a/compiler/tests/sram1.gds and /dev/null differ diff --git a/compiler/tests/sram1.lef b/compiler/tests/sram1.lef deleted file mode 100644 index c0563063..00000000 --- a/compiler/tests/sram1.lef +++ /dev/null @@ -1,19 +0,0 @@ -VERSION 5.4 ; -NAMESCASESENSITIVE ON ; -BUSBITCHARS "[]" ; -DIVIDERCHAR "/" ; -UNITS - DATABASE MICRONS 1000 ; -END UNITS -SITE MacroSite - CLASS Core ; - SIZE 324000.0 by 421500.0 ; -END MacroSite -MACRO sram1 - CLASS BLOCK ; - SIZE 324000.0 BY 421500.0 ; - SYMMETRY X Y R90 ; - SITE MacroSite ; - PIN DIN[0] - DIRECTION INPUT ; - PORT diff --git a/compiler/tests/sram1.sp b/compiler/tests/sram1.sp deleted file mode 100644 index 622af672..00000000 --- a/compiler/tests/sram1.sp +++ /dev/null @@ -1,602 +0,0 @@ -************************************************** -* OpenRAM generated memory. -* Words: 16 -* Data bits: 4 -* Banks: 1 -* Column mux: 1:1 -************************************************** -* Positive edge-triggered FF -.subckt dff D Q clk vdd gnd -M0 vdd clk a_2_6# vdd p w=12u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M1 a_17_74# D vdd vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M2 a_22_6# clk a_17_74# vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M3 a_31_74# a_2_6# a_22_6# vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M4 vdd a_34_4# a_31_74# vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M5 a_34_4# a_22_6# vdd vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M6 a_61_74# a_34_4# vdd vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M7 a_66_6# a_2_6# a_61_74# vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M8 a_76_84# clk a_66_6# vdd p w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M9 vdd Q a_76_84# vdd p w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M10 gnd clk a_2_6# gnd n w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M11 Q a_66_6# vdd vdd p w=12u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M12 a_17_6# D gnd gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M13 a_22_6# a_2_6# a_17_6# gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M14 a_31_6# clk a_22_6# gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M15 gnd a_34_4# a_31_6# gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M16 a_34_4# a_22_6# gnd gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M17 a_61_6# a_34_4# gnd gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M18 a_66_6# clk a_61_6# gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M19 a_76_6# a_2_6# a_66_6# gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M20 gnd Q a_76_6# gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M21 Q a_66_6# gnd gnd n w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -.ends dff - -* ptx M{0} {1} n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p - -* ptx M{0} {1} p m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p - -.SUBCKT pinv_2 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p -Mpinv_nmos Z A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pinv_2 - -.SUBCKT dff_inv_2 D Q Qb clk vdd gnd -Xdff_inv_dff D Q clk vdd gnd dff -Xdff_inv_inv1 Q Qb vdd gnd pinv_2 -.ENDS dff_inv_2 - -.SUBCKT dff_array_3x1 din[0] din[1] din[2] dout[0] dout_bar[0] dout[1] dout_bar[1] dout[2] dout_bar[2] clk vdd gnd -XXdff_r0_c0 din[0] dout[0] dout_bar[0] clk vdd gnd dff_inv_2 -XXdff_r1_c0 din[1] dout[1] dout_bar[1] clk vdd gnd dff_inv_2 -XXdff_r2_c0 din[2] dout[2] dout_bar[2] clk vdd gnd dff_inv_2 -.ENDS dff_array_3x1 - -* ptx M{0} {1} p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p - -.SUBCKT pnand2_1 A B Z vdd gnd -Mpnand2_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos1 Z B net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos2 net1 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand2_1 - -.SUBCKT pnand3_1 A B C Z vdd gnd -Mpnand3_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_pmos3 Z C vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos1 Z C net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos2 net1 B net2 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos3 net2 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand3_1 - -* ptx M{0} {1} n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p - -.SUBCKT pinv_3 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_3 - -* ptx M{0} {1} n m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p - -* ptx M{0} {1} p m=1 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p - -.SUBCKT pinv_4 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p -Mpinv_nmos Z A gnd gnd n m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p -.ENDS pinv_4 - -* ptx M{0} {1} n m=4 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p - -* ptx M{0} {1} p m=4 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p - -.SUBCKT pinv_5 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=4 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p -Mpinv_nmos Z A gnd gnd n m=4 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p -.ENDS pinv_5 - -.SUBCKT pinvbuf_4_16 A Zb Z vdd gnd -Xbuf_inv1 A zb_int vdd gnd pinv_3 -Xbuf_inv2 zb_int z_int vdd gnd pinv_4 -Xbuf_inv3 z_int Zb vdd gnd pinv_5 -Xbuf_inv4 zb_int Z vdd gnd pinv_5 -.ENDS pinvbuf_4_16 - -.SUBCKT pinv_6 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_6 - -.SUBCKT pinv_7 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p -Mpinv_nmos Z A gnd gnd n m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p -.ENDS pinv_7 - -.SUBCKT pinv_8 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=4 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p -Mpinv_nmos Z A gnd gnd n m=4 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p -.ENDS pinv_8 - -*********************** "cell_6t" ****************************** -.SUBCKT replica_cell_6t bl br wl vdd gnd -M_1 gnd net_2 vdd vdd p W='0.9u' L=1.2u -M_2 net_2 gnd 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 gnd gnd n W='1.2u' L=0.6u -M_5 net_2 gnd gnd gnd n W='2.4u' L=0.6u -M_6 gnd net_2 gnd gnd n W='2.4u' L=0.6u -.ENDS $ replica_cell_6t - -*********************** "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 -.ENDS $ cell_6t - -.SUBCKT bitline_load bl[0] br[0] wl[0] wl[1] wl[2] wl[3] vdd gnd -Xbit_r0_c0 bl[0] br[0] wl[0] vdd gnd cell_6t -Xbit_r1_c0 bl[0] br[0] wl[1] vdd gnd cell_6t -Xbit_r2_c0 bl[0] br[0] wl[2] vdd gnd cell_6t -Xbit_r3_c0 bl[0] br[0] wl[3] vdd gnd cell_6t -.ENDS bitline_load - -.SUBCKT pinv_9 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_9 - -.SUBCKT delay_chain in out vdd gnd -Xdinv0 in dout_1 vdd gnd pinv_9 -Xdload_0_0 dout_1 n_0_0 vdd gnd pinv_9 -Xdload_0_1 dout_1 n_0_1 vdd gnd pinv_9 -Xdload_0_2 dout_1 n_0_2 vdd gnd pinv_9 -Xdinv1 dout_1 dout_2 vdd gnd pinv_9 -Xdload_1_0 dout_2 n_1_0 vdd gnd pinv_9 -Xdload_1_1 dout_2 n_1_1 vdd gnd pinv_9 -Xdload_1_2 dout_2 n_1_2 vdd gnd pinv_9 -Xdinv2 dout_2 out vdd gnd pinv_9 -Xdload_2_0 out n_2_0 vdd gnd pinv_9 -Xdload_2_1 out n_2_1 vdd gnd pinv_9 -Xdload_2_2 out n_2_2 vdd gnd pinv_9 -.ENDS delay_chain - -.SUBCKT pinv_10 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_10 - -* ptx M{0} {1} p m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p - -.SUBCKT replica_bitline en out vdd gnd -Xrbl_inv bl[0] out vdd gnd pinv_10 -Mrbl_access_tx vdd delayed_en bl[0] vdd p m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -Xdelay_chain en delayed_en vdd gnd delay_chain -Xbitcell bl[0] br[0] delayed_en vdd gnd replica_cell_6t -Xload bl[0] br[0] gnd gnd gnd gnd vdd gnd bitline_load -.ENDS replica_bitline - -.SUBCKT control_logic csb web oeb clk s_en w_en tri_en tri_en_bar clk_buf_bar clk_buf vdd gnd -Xctrl_dffs csb web oeb cs_bar cs we_bar we oe_bar oe clk_buf vdd gnd dff_array_3x1 -Xclkbuf clk clk_buf_bar clk_buf vdd gnd pinvbuf_4_16 -Xnand3_w_en_bar clk_buf_bar cs we w_en_bar vdd gnd pnand3_1 -Xinv_pre_w_en w_en_bar pre_w_en vdd gnd pinv_6 -Xinv_pre_w_en_bar pre_w_en pre_w_en_bar vdd gnd pinv_7 -Xinv_w_en2 pre_w_en_bar w_en vdd gnd pinv_8 -Xinv_tri_en1 pre_tri_en_bar pre_tri_en1 vdd gnd pinv_7 -Xtri_en_buf1 pre_tri_en1 pre_tri_en_bar1 vdd gnd pinv_7 -Xtri_en_buf2 pre_tri_en_bar1 tri_en vdd gnd pinv_8 -Xnand2_tri_en clk_buf_bar oe pre_tri_en_bar vdd gnd pnand2_1 -Xtri_en_bar_buf1 pre_tri_en_bar pre_tri_en2 vdd gnd pinv_7 -Xtri_en_bar_buf2 pre_tri_en2 tri_en_bar vdd gnd pinv_8 -Xnand3_rblk_bar clk_buf_bar oe cs rblk_bar vdd gnd pnand3_1 -Xinv_rblk rblk_bar rblk vdd gnd pinv_6 -Xinv_s_en pre_s_en_bar s_en vdd gnd pinv_8 -Xinv_pre_s_en_bar pre_s_en pre_s_en_bar vdd gnd pinv_7 -Xreplica_bitline rblk pre_s_en vdd gnd replica_bitline -.ENDS control_logic - -.SUBCKT dff_array din[0] din[1] din[2] din[3] dout[0] dout[1] dout[2] dout[3] clk vdd gnd -XXdff_r0_c0 din[0] dout[0] clk vdd gnd dff -XXdff_r1_c0 din[1] dout[1] clk vdd gnd dff -XXdff_r2_c0 din[2] dout[2] clk vdd gnd dff -XXdff_r3_c0 din[3] dout[3] clk vdd gnd dff -.ENDS dff_array - -.SUBCKT bitcell_array bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] vdd gnd -Xbit_r0_c0 bl[0] br[0] wl[0] vdd gnd cell_6t -Xbit_r1_c0 bl[0] br[0] wl[1] vdd gnd cell_6t -Xbit_r2_c0 bl[0] br[0] wl[2] vdd gnd cell_6t -Xbit_r3_c0 bl[0] br[0] wl[3] vdd gnd cell_6t -Xbit_r4_c0 bl[0] br[0] wl[4] vdd gnd cell_6t -Xbit_r5_c0 bl[0] br[0] wl[5] vdd gnd cell_6t -Xbit_r6_c0 bl[0] br[0] wl[6] vdd gnd cell_6t -Xbit_r7_c0 bl[0] br[0] wl[7] vdd gnd cell_6t -Xbit_r8_c0 bl[0] br[0] wl[8] vdd gnd cell_6t -Xbit_r9_c0 bl[0] br[0] wl[9] vdd gnd cell_6t -Xbit_r10_c0 bl[0] br[0] wl[10] vdd gnd cell_6t -Xbit_r11_c0 bl[0] br[0] wl[11] vdd gnd cell_6t -Xbit_r12_c0 bl[0] br[0] wl[12] vdd gnd cell_6t -Xbit_r13_c0 bl[0] br[0] wl[13] vdd gnd cell_6t -Xbit_r14_c0 bl[0] br[0] wl[14] vdd gnd cell_6t -Xbit_r15_c0 bl[0] br[0] wl[15] vdd gnd cell_6t -Xbit_r0_c1 bl[1] br[1] wl[0] vdd gnd cell_6t -Xbit_r1_c1 bl[1] br[1] wl[1] vdd gnd cell_6t -Xbit_r2_c1 bl[1] br[1] wl[2] vdd gnd cell_6t -Xbit_r3_c1 bl[1] br[1] wl[3] vdd gnd cell_6t -Xbit_r4_c1 bl[1] br[1] wl[4] vdd gnd cell_6t -Xbit_r5_c1 bl[1] br[1] wl[5] vdd gnd cell_6t -Xbit_r6_c1 bl[1] br[1] wl[6] vdd gnd cell_6t -Xbit_r7_c1 bl[1] br[1] wl[7] vdd gnd cell_6t -Xbit_r8_c1 bl[1] br[1] wl[8] vdd gnd cell_6t -Xbit_r9_c1 bl[1] br[1] wl[9] vdd gnd cell_6t -Xbit_r10_c1 bl[1] br[1] wl[10] vdd gnd cell_6t -Xbit_r11_c1 bl[1] br[1] wl[11] vdd gnd cell_6t -Xbit_r12_c1 bl[1] br[1] wl[12] vdd gnd cell_6t -Xbit_r13_c1 bl[1] br[1] wl[13] vdd gnd cell_6t -Xbit_r14_c1 bl[1] br[1] wl[14] vdd gnd cell_6t -Xbit_r15_c1 bl[1] br[1] wl[15] vdd gnd cell_6t -Xbit_r0_c2 bl[2] br[2] wl[0] vdd gnd cell_6t -Xbit_r1_c2 bl[2] br[2] wl[1] vdd gnd cell_6t -Xbit_r2_c2 bl[2] br[2] wl[2] vdd gnd cell_6t -Xbit_r3_c2 bl[2] br[2] wl[3] vdd gnd cell_6t -Xbit_r4_c2 bl[2] br[2] wl[4] vdd gnd cell_6t -Xbit_r5_c2 bl[2] br[2] wl[5] vdd gnd cell_6t -Xbit_r6_c2 bl[2] br[2] wl[6] vdd gnd cell_6t -Xbit_r7_c2 bl[2] br[2] wl[7] vdd gnd cell_6t -Xbit_r8_c2 bl[2] br[2] wl[8] vdd gnd cell_6t -Xbit_r9_c2 bl[2] br[2] wl[9] vdd gnd cell_6t -Xbit_r10_c2 bl[2] br[2] wl[10] vdd gnd cell_6t -Xbit_r11_c2 bl[2] br[2] wl[11] vdd gnd cell_6t -Xbit_r12_c2 bl[2] br[2] wl[12] vdd gnd cell_6t -Xbit_r13_c2 bl[2] br[2] wl[13] vdd gnd cell_6t -Xbit_r14_c2 bl[2] br[2] wl[14] vdd gnd cell_6t -Xbit_r15_c2 bl[2] br[2] wl[15] vdd gnd cell_6t -Xbit_r0_c3 bl[3] br[3] wl[0] vdd gnd cell_6t -Xbit_r1_c3 bl[3] br[3] wl[1] vdd gnd cell_6t -Xbit_r2_c3 bl[3] br[3] wl[2] vdd gnd cell_6t -Xbit_r3_c3 bl[3] br[3] wl[3] vdd gnd cell_6t -Xbit_r4_c3 bl[3] br[3] wl[4] vdd gnd cell_6t -Xbit_r5_c3 bl[3] br[3] wl[5] vdd gnd cell_6t -Xbit_r6_c3 bl[3] br[3] wl[6] vdd gnd cell_6t -Xbit_r7_c3 bl[3] br[3] wl[7] vdd gnd cell_6t -Xbit_r8_c3 bl[3] br[3] wl[8] vdd gnd cell_6t -Xbit_r9_c3 bl[3] br[3] wl[9] vdd gnd cell_6t -Xbit_r10_c3 bl[3] br[3] wl[10] vdd gnd cell_6t -Xbit_r11_c3 bl[3] br[3] wl[11] vdd gnd cell_6t -Xbit_r12_c3 bl[3] br[3] wl[12] vdd gnd cell_6t -Xbit_r13_c3 bl[3] br[3] wl[13] vdd gnd cell_6t -Xbit_r14_c3 bl[3] br[3] wl[14] vdd gnd cell_6t -Xbit_r15_c3 bl[3] br[3] wl[15] vdd gnd cell_6t -.ENDS bitcell_array - -* ptx M{0} {1} p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p - -.SUBCKT precharge bl br en vdd -Mlower_pmos bl en br vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mupper_pmos1 bl en vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mupper_pmos2 br en vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS precharge - -.SUBCKT precharge_array bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] en vdd -Xpre_column_0 bl[0] br[0] en vdd precharge -Xpre_column_1 bl[1] br[1] en vdd precharge -Xpre_column_2 bl[2] br[2] en vdd precharge -Xpre_column_3 bl[3] br[3] en vdd precharge -.ENDS precharge_array -*********************** "sense_amp" ****************************** - -.SUBCKT sense_amp bl br dout en vdd gnd -M_1 dout net_1 vdd vdd p W='5.4*1u' L=0.6u -M_2 dout net_1 net_2 gnd n W='2.7*1u' L=0.6u -M_3 net_1 dout vdd vdd p W='5.4*1u' L=0.6u -M_4 net_1 dout net_2 gnd n W='2.7*1u' L=0.6u -M_5 bl en dout vdd p W='7.2*1u' L=0.6u -M_6 br en net_1 vdd p W='7.2*1u' L=0.6u -M_7 net_2 en gnd gnd n W='2.7*1u' L=0.6u -.ENDS sense_amp - - -.SUBCKT sense_amp_array data[0] bl[0] br[0] data[1] bl[1] br[1] data[2] bl[2] br[2] data[3] bl[3] br[3] en vdd gnd -Xsa_d0 bl[0] br[0] data[0] en vdd gnd sense_amp -Xsa_d1 bl[1] br[1] data[1] en vdd gnd sense_amp -Xsa_d2 bl[2] br[2] data[2] en vdd gnd sense_amp -Xsa_d3 bl[3] br[3] data[3] en vdd gnd sense_amp -.ENDS sense_amp_array -*********************** Write_Driver ****************************** -.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 - -**** 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_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_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 - -**** 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_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_15 bl net_6 net_5 gnd n W='3.6*1u' L=0.6u -M_16 br net_1 net_5 gnd n W='3.6*1u' L=0.6u -M_17 net_5 en gnd gnd n W='3.6*1u' L=0.6u - - - -.ENDS $ write_driver - - -.SUBCKT write_driver_array data[0] data[1] data[2] data[3] bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] en vdd gnd -XXwrite_driver0 data[0] bl[0] br[0] en vdd gnd write_driver -XXwrite_driver1 data[1] bl[1] br[1] en vdd gnd write_driver -XXwrite_driver2 data[2] bl[2] br[2] en vdd gnd write_driver -XXwrite_driver3 data[3] bl[3] br[3] en vdd gnd write_driver -.ENDS write_driver_array - -.SUBCKT pinv_11 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_11 - -.SUBCKT pnand2_2 A B Z vdd gnd -Mpnand2_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos1 Z B net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos2 net1 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand2_2 - -.SUBCKT pnand3_2 A B C Z vdd gnd -Mpnand3_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_pmos3 Z C vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos1 Z C net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos2 net1 B net2 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos3 net2 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand3_2 - -.SUBCKT pinv_12 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_12 - -.SUBCKT pnand2_3 A B Z vdd gnd -Mpnand2_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos1 Z B net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos2 net1 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand2_3 - -.SUBCKT pre2x4 in[0] in[1] out[0] out[1] out[2] out[3] vdd gnd -XXpre_inv[0] in[0] inbar[0] vdd gnd pinv_12 -XXpre_inv[1] in[1] inbar[1] vdd gnd pinv_12 -XXpre_nand_inv[0] Z[0] out[0] vdd gnd pinv_12 -XXpre_nand_inv[1] Z[1] out[1] vdd gnd pinv_12 -XXpre_nand_inv[2] Z[2] out[2] vdd gnd pinv_12 -XXpre_nand_inv[3] Z[3] out[3] vdd gnd pinv_12 -XXpre2x4_nand[0] inbar[0] inbar[1] Z[0] vdd gnd pnand2_3 -XXpre2x4_nand[1] in[0] inbar[1] Z[1] vdd gnd pnand2_3 -XXpre2x4_nand[2] inbar[0] in[1] Z[2] vdd gnd pnand2_3 -XXpre2x4_nand[3] in[0] in[1] Z[3] vdd gnd pnand2_3 -.ENDS pre2x4 - -.SUBCKT pinv_13 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_13 - -.SUBCKT pnand3_3 A B C Z vdd gnd -Mpnand3_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_pmos3 Z C vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos1 Z C net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos2 net1 B net2 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos3 net2 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand3_3 - -.SUBCKT pre3x8 in[0] in[1] in[2] out[0] out[1] out[2] out[3] out[4] out[5] out[6] out[7] vdd gnd -XXpre_inv[0] in[0] inbar[0] vdd gnd pinv_13 -XXpre_inv[1] in[1] inbar[1] vdd gnd pinv_13 -XXpre_inv[2] in[2] inbar[2] vdd gnd pinv_13 -XXpre_nand_inv[0] Z[0] out[0] vdd gnd pinv_13 -XXpre_nand_inv[1] Z[1] out[1] vdd gnd pinv_13 -XXpre_nand_inv[2] Z[2] out[2] vdd gnd pinv_13 -XXpre_nand_inv[3] Z[3] out[3] vdd gnd pinv_13 -XXpre_nand_inv[4] Z[4] out[4] vdd gnd pinv_13 -XXpre_nand_inv[5] Z[5] out[5] vdd gnd pinv_13 -XXpre_nand_inv[6] Z[6] out[6] vdd gnd pinv_13 -XXpre_nand_inv[7] Z[7] out[7] vdd gnd pinv_13 -XXpre3x8_nand[0] inbar[0] inbar[1] inbar[2] Z[0] vdd gnd pnand3_3 -XXpre3x8_nand[1] in[0] inbar[1] inbar[2] Z[1] vdd gnd pnand3_3 -XXpre3x8_nand[2] inbar[0] in[1] inbar[2] Z[2] vdd gnd pnand3_3 -XXpre3x8_nand[3] in[0] in[1] inbar[2] Z[3] vdd gnd pnand3_3 -XXpre3x8_nand[4] inbar[0] inbar[1] in[2] Z[4] vdd gnd pnand3_3 -XXpre3x8_nand[5] in[0] inbar[1] in[2] Z[5] vdd gnd pnand3_3 -XXpre3x8_nand[6] inbar[0] in[1] in[2] Z[6] vdd gnd pnand3_3 -XXpre3x8_nand[7] in[0] in[1] in[2] Z[7] vdd gnd pnand3_3 -.ENDS pre3x8 - -.SUBCKT hierarchical_decoder_16rows A[0] A[1] A[2] A[3] decode[0] decode[1] decode[2] decode[3] decode[4] decode[5] decode[6] decode[7] decode[8] decode[9] decode[10] decode[11] decode[12] decode[13] decode[14] decode[15] vdd gnd -Xpre[0] A[0] A[1] out[0] out[1] out[2] out[3] vdd gnd pre2x4 -Xpre[1] A[2] A[3] out[4] out[5] out[6] out[7] vdd gnd pre2x4 -XDEC_NAND[0] out[0] out[4] Z[0] vdd gnd pnand2_2 -XDEC_NAND[1] out[0] out[5] Z[1] vdd gnd pnand2_2 -XDEC_NAND[2] out[0] out[6] Z[2] vdd gnd pnand2_2 -XDEC_NAND[3] out[0] out[7] Z[3] vdd gnd pnand2_2 -XDEC_NAND[4] out[1] out[4] Z[4] vdd gnd pnand2_2 -XDEC_NAND[5] out[1] out[5] Z[5] vdd gnd pnand2_2 -XDEC_NAND[6] out[1] out[6] Z[6] vdd gnd pnand2_2 -XDEC_NAND[7] out[1] out[7] Z[7] vdd gnd pnand2_2 -XDEC_NAND[8] out[2] out[4] Z[8] vdd gnd pnand2_2 -XDEC_NAND[9] out[2] out[5] Z[9] vdd gnd pnand2_2 -XDEC_NAND[10] out[2] out[6] Z[10] vdd gnd pnand2_2 -XDEC_NAND[11] out[2] out[7] Z[11] vdd gnd pnand2_2 -XDEC_NAND[12] out[3] out[4] Z[12] vdd gnd pnand2_2 -XDEC_NAND[13] out[3] out[5] Z[13] vdd gnd pnand2_2 -XDEC_NAND[14] out[3] out[6] Z[14] vdd gnd pnand2_2 -XDEC_NAND[15] out[3] out[7] Z[15] vdd gnd pnand2_2 -XDEC_INV_[0] Z[0] decode[0] vdd gnd pinv_11 -XDEC_INV_[1] Z[1] decode[1] vdd gnd pinv_11 -XDEC_INV_[2] Z[2] decode[2] vdd gnd pinv_11 -XDEC_INV_[3] Z[3] decode[3] vdd gnd pinv_11 -XDEC_INV_[4] Z[4] decode[4] vdd gnd pinv_11 -XDEC_INV_[5] Z[5] decode[5] vdd gnd pinv_11 -XDEC_INV_[6] Z[6] decode[6] vdd gnd pinv_11 -XDEC_INV_[7] Z[7] decode[7] vdd gnd pinv_11 -XDEC_INV_[8] Z[8] decode[8] vdd gnd pinv_11 -XDEC_INV_[9] Z[9] decode[9] vdd gnd pinv_11 -XDEC_INV_[10] Z[10] decode[10] vdd gnd pinv_11 -XDEC_INV_[11] Z[11] decode[11] vdd gnd pinv_11 -XDEC_INV_[12] Z[12] decode[12] vdd gnd pinv_11 -XDEC_INV_[13] Z[13] decode[13] vdd gnd pinv_11 -XDEC_INV_[14] Z[14] decode[14] vdd gnd pinv_11 -XDEC_INV_[15] Z[15] decode[15] vdd gnd pinv_11 -.ENDS hierarchical_decoder_16rows -*********************** tri_gate ****************************** - -.SUBCKT tri_gate in out en en_bar vdd gnd - -M_1 net_2 in_inv gnd gnd n W='1.2*1u' L=0.6u -M_2 net_3 in_inv vdd vdd p W='2.4*1u' L=0.6u -M_3 out en_bar net_3 vdd p W='2.4*1u' L=0.6u -M_4 out en net_2 gnd n W='1.2*1u' L=0.6u -M_5 in_inv in vdd vdd p W='2.4*1u' L=0.6u -M_6 in_inv in gnd gnd n W='1.2*1u' L=0.6u - - -.ENDS - -.SUBCKT tri_gate_array in[0] in[1] in[2] in[3] out[0] out[1] out[2] out[3] en en_bar vdd gnd -XXtri_gate0 in[0] out[0] en en_bar vdd gnd tri_gate -XXtri_gate1 in[1] out[1] en en_bar vdd gnd tri_gate -XXtri_gate2 in[2] out[2] en en_bar vdd gnd tri_gate -XXtri_gate3 in[3] out[3] en en_bar vdd gnd tri_gate -.ENDS tri_gate_array - -.SUBCKT pinv_14 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_14 - -.SUBCKT pinv_15 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_15 - -.SUBCKT pnand2_4 A B Z vdd gnd -Mpnand2_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos1 Z B net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos2 net1 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand2_4 - -.SUBCKT wordline_driver in[0] in[1] in[2] in[3] in[4] in[5] in[6] in[7] in[8] in[9] in[10] in[11] in[12] in[13] in[14] in[15] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] en vdd gnd -Xwl_driver_inv_en0 en en_bar[0] vdd gnd pinv_15 -Xwl_driver_nand0 en_bar[0] in[0] net[0] vdd gnd pnand2_4 -Xwl_driver_inv0 net[0] wl[0] vdd gnd pinv_14 -Xwl_driver_inv_en1 en en_bar[1] vdd gnd pinv_15 -Xwl_driver_nand1 en_bar[1] in[1] net[1] vdd gnd pnand2_4 -Xwl_driver_inv1 net[1] wl[1] vdd gnd pinv_14 -Xwl_driver_inv_en2 en en_bar[2] vdd gnd pinv_15 -Xwl_driver_nand2 en_bar[2] in[2] net[2] vdd gnd pnand2_4 -Xwl_driver_inv2 net[2] wl[2] vdd gnd pinv_14 -Xwl_driver_inv_en3 en en_bar[3] vdd gnd pinv_15 -Xwl_driver_nand3 en_bar[3] in[3] net[3] vdd gnd pnand2_4 -Xwl_driver_inv3 net[3] wl[3] vdd gnd pinv_14 -Xwl_driver_inv_en4 en en_bar[4] vdd gnd pinv_15 -Xwl_driver_nand4 en_bar[4] in[4] net[4] vdd gnd pnand2_4 -Xwl_driver_inv4 net[4] wl[4] vdd gnd pinv_14 -Xwl_driver_inv_en5 en en_bar[5] vdd gnd pinv_15 -Xwl_driver_nand5 en_bar[5] in[5] net[5] vdd gnd pnand2_4 -Xwl_driver_inv5 net[5] wl[5] vdd gnd pinv_14 -Xwl_driver_inv_en6 en en_bar[6] vdd gnd pinv_15 -Xwl_driver_nand6 en_bar[6] in[6] net[6] vdd gnd pnand2_4 -Xwl_driver_inv6 net[6] wl[6] vdd gnd pinv_14 -Xwl_driver_inv_en7 en en_bar[7] vdd gnd pinv_15 -Xwl_driver_nand7 en_bar[7] in[7] net[7] vdd gnd pnand2_4 -Xwl_driver_inv7 net[7] wl[7] vdd gnd pinv_14 -Xwl_driver_inv_en8 en en_bar[8] vdd gnd pinv_15 -Xwl_driver_nand8 en_bar[8] in[8] net[8] vdd gnd pnand2_4 -Xwl_driver_inv8 net[8] wl[8] vdd gnd pinv_14 -Xwl_driver_inv_en9 en en_bar[9] vdd gnd pinv_15 -Xwl_driver_nand9 en_bar[9] in[9] net[9] vdd gnd pnand2_4 -Xwl_driver_inv9 net[9] wl[9] vdd gnd pinv_14 -Xwl_driver_inv_en10 en en_bar[10] vdd gnd pinv_15 -Xwl_driver_nand10 en_bar[10] in[10] net[10] vdd gnd pnand2_4 -Xwl_driver_inv10 net[10] wl[10] vdd gnd pinv_14 -Xwl_driver_inv_en11 en en_bar[11] vdd gnd pinv_15 -Xwl_driver_nand11 en_bar[11] in[11] net[11] vdd gnd pnand2_4 -Xwl_driver_inv11 net[11] wl[11] vdd gnd pinv_14 -Xwl_driver_inv_en12 en en_bar[12] vdd gnd pinv_15 -Xwl_driver_nand12 en_bar[12] in[12] net[12] vdd gnd pnand2_4 -Xwl_driver_inv12 net[12] wl[12] vdd gnd pinv_14 -Xwl_driver_inv_en13 en en_bar[13] vdd gnd pinv_15 -Xwl_driver_nand13 en_bar[13] in[13] net[13] vdd gnd pnand2_4 -Xwl_driver_inv13 net[13] wl[13] vdd gnd pinv_14 -Xwl_driver_inv_en14 en en_bar[14] vdd gnd pinv_15 -Xwl_driver_nand14 en_bar[14] in[14] net[14] vdd gnd pnand2_4 -Xwl_driver_inv14 net[14] wl[14] vdd gnd pinv_14 -Xwl_driver_inv_en15 en en_bar[15] vdd gnd pinv_15 -Xwl_driver_nand15 en_bar[15] in[15] net[15] vdd gnd pnand2_4 -Xwl_driver_inv15 net[15] wl[15] vdd gnd pinv_14 -.ENDS wordline_driver - -.SUBCKT pinv_16 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_16 - -.SUBCKT bank DOUT[0] DOUT[1] DOUT[2] DOUT[3] DIN[0] DIN[1] DIN[2] DIN[3] A[0] A[1] A[2] A[3] s_en w_en tri_en_bar tri_en clk_buf_bar clk_buf vdd gnd -Xbitcell_array bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] vdd gnd bitcell_array -Xprecharge_array bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] clk_buf_bar vdd precharge_array -Xsense_amp_array sa_out[0] bl[0] br[0] sa_out[1] bl[1] br[1] sa_out[2] bl[2] br[2] sa_out[3] bl[3] br[3] s_en vdd gnd sense_amp_array -Xwrite_driver_array DIN[0] DIN[1] DIN[2] DIN[3] bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] w_en vdd gnd write_driver_array -Xtri_gate_array sa_out[0] sa_out[1] sa_out[2] sa_out[3] DOUT[0] DOUT[1] DOUT[2] DOUT[3] tri_en tri_en_bar vdd gnd tri_gate_array -Xrow_decoder A[0] A[1] A[2] A[3] dec_out[0] dec_out[1] dec_out[2] dec_out[3] dec_out[4] dec_out[5] dec_out[6] dec_out[7] dec_out[8] dec_out[9] dec_out[10] dec_out[11] dec_out[12] dec_out[13] dec_out[14] dec_out[15] vdd gnd hierarchical_decoder_16rows -Xwordline_driver dec_out[0] dec_out[1] dec_out[2] dec_out[3] dec_out[4] dec_out[5] dec_out[6] dec_out[7] dec_out[8] dec_out[9] dec_out[10] dec_out[11] dec_out[12] dec_out[13] dec_out[14] dec_out[15] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] clk_buf vdd gnd wordline_driver -.ENDS bank - -.SUBCKT sram1 DIN[0] DIN[1] DIN[2] DIN[3] ADDR[0] ADDR[1] ADDR[2] ADDR[3] csb web oeb clk DOUT[0] DOUT[1] DOUT[2] DOUT[3] vdd gnd -Xbank0 DOUT[0] DOUT[1] DOUT[2] DOUT[3] DIN[0] DIN[1] DIN[2] DIN[3] A[0] A[1] A[2] A[3] s_en w_en tri_en_bar tri_en clk_buf_bar clk_buf vdd gnd bank -Xcontrol csb_s web_s oeb_s clk s_en w_en tri_en tri_en_bar clk_buf_bar clk_buf vdd gnd control_logic -Xaddress ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A[1] A[2] A[3] clk_buf vdd gnd dff_array -Xaddress ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A[1] A[2] A[3] clk_buf vdd gnd dff_array -.ENDS sram1 diff --git a/compiler/tests/sram1_TT_5p0V_25C.lib b/compiler/tests/sram1_TT_5p0V_25C.lib deleted file mode 100644 index ddf17785..00000000 --- a/compiler/tests/sram1_TT_5p0V_25C.lib +++ /dev/null @@ -1,347 +0,0 @@ -library (sram1_TT_5p0V_25C_lib){ - delay_model : "table_lookup"; - time_unit : "1ns" ; - voltage_unit : "1v" ; - current_unit : "1mA" ; - resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; - leakage_power_unit : "1mW" ; - pulling_resistance_unit :"1kohm" ; - operating_conditions(OC){ - process : 1.0 ; - voltage : 5.0 ; - temperature : 25; - } - - input_threshold_pct_fall : 50.0 ; - output_threshold_pct_fall : 50.0 ; - input_threshold_pct_rise : 50.0 ; - output_threshold_pct_rise : 50.0 ; - slew_lower_threshold_pct_fall : 10.0 ; - slew_upper_threshold_pct_fall : 90.0 ; - slew_lower_threshold_pct_rise : 10.0 ; - slew_upper_threshold_pct_rise : 90.0 ; - - nom_voltage : 5.0; - nom_temperature : 25; - nom_process : 1.0; - default_cell_leakage_power : 0.0 ; - default_leakage_power_density : 0.0 ; - default_input_pin_cap : 1.0 ; - default_inout_pin_cap : 1.0 ; - default_output_pin_cap : 0.0 ; - default_max_transition : 0.5 ; - default_fanout_load : 1.0 ; - default_max_fanout : 4.0 ; - default_connection_class : universal ; - - lu_table_template(CELL_TABLE){ - variable_1 : input_net_transition; - variable_2 : total_output_net_capacitance; - index_1("0.0125, 0.05, 0.4"); - index_2("2.45605, 9.8242, 78.5936"); - } - - lu_table_template(CONSTRAINT_TABLE){ - variable_1 : related_pin_transition; - variable_2 : constrained_pin_transition; - index_1("0.0125, 0.05, 0.4"); - index_2("0.0125, 0.05, 0.4"); - } - - default_operating_conditions : OC; - - - type (DATA){ - base_type : array; - data_type : bit; - bit_width : 4; - bit_from : 0; - bit_to : 3; - } - - type (ADDR){ - base_type : array; - data_type : bit; - bit_width : 4; - bit_from : 0; - bit_to : 3; - } - -cell (sram1){ - memory(){ - type : ram; - address_width : 4; - word_width : 4; - } - interface_timing : true; - dont_use : true; - map_only : true; - dont_touch : true; - area : 136566.0; - - leakage_power () { - when : "CSb"; - value : 0.000202; - } - cell_leakage_power : 0; - bus(DATA){ - bus_type : DATA; - direction : inout; - max_capacitance : 78.5936; - min_capacitance : 2.45605; - three_state : "!OEb & !clk"; - memory_write(){ - address : ADDR; - clocked_on : clk; - } - memory_read(){ - address : ADDR; - } - pin(DATA[3:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - timing(){ - timing_sense : non_unate; - related_pin : "clk"; - timing_type : falling_edge; - cell_rise(CELL_TABLE) { - values("0.612, 0.66, 1.1",\ - "0.612, 0.66, 1.1",\ - "0.612, 0.66, 1.1"); - } - cell_fall(CELL_TABLE) { - values("0.612, 0.66, 1.1",\ - "0.612, 0.66, 1.1",\ - "0.612, 0.66, 1.1"); - } - rise_transition(CELL_TABLE) { - values("0.024, 0.081, 0.61",\ - "0.024, 0.081, 0.61",\ - "0.024, 0.081, 0.61"); - } - fall_transition(CELL_TABLE) { - values("0.024, 0.081, 0.61",\ - "0.024, 0.081, 0.61",\ - "0.024, 0.081, 0.61"); - } - } - } - } - - bus(ADDR){ - bus_type : ADDR; - direction : input; - capacitance : 9.8242; - max_transition : 0.4; - pin(ADDR[3:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - } - } - - pin(CSb){ - direction : input; - capacitance : 9.8242; - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - } - - pin(OEb){ - direction : input; - capacitance : 9.8242; - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - } - - pin(WEb){ - direction : input; - capacitance : 9.8242; - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - } - - pin(clk){ - clock : true; - direction : input; - capacitance : 9.8242; - internal_power(){ - when : "!CSb & clk & !WEb"; - rise_power(scalar){ - values("10.812808757533329"); - } - fall_power(scalar){ - values("10.812808757533329"); - } - } - internal_power(){ - when : "!CSb & !clk & WEb"; - rise_power(scalar){ - values("10.812808757533329"); - } - fall_power(scalar){ - values("10.812808757533329"); - } - } - internal_power(){ - when : "CSb"; - rise_power(scalar){ - values("0"); - } - fall_power(scalar){ - values("0"); - } - } - timing(){ - timing_type :"min_pulse_width"; - related_pin : clk; - rise_constraint(scalar) { - values("0.0"); - } - fall_constraint(scalar) { - values("0.0"); - } - } - timing(){ - timing_type :"minimum_period"; - related_pin : clk; - rise_constraint(scalar) { - values("0"); - } - fall_constraint(scalar) { - values("0"); - } - } - } - } -} diff --git a/compiler/tests/sram_1rw_1r_tb.v b/compiler/tests/sram_1rw_1r_tb.v new file mode 100644 index 00000000..9c212e0a --- /dev/null +++ b/compiler/tests/sram_1rw_1r_tb.v @@ -0,0 +1,143 @@ +`define assert(signal, value) \ +if (!(signal === value)) begin \ + $display("ASSERTION FAILED in %m: signal != value"); \ + $finish;\ +end + +module sram_1rw_1r_tb; + reg clk; + + // 1rw port + reg [3:0] addr0; + reg [1:0] din0; + reg csb0; + reg web0; + wire [1:0] dout0; + + // 1r port + reg [3:0] addr1; + reg csb1; + wire [1:0] dout1; + + sram_1rw_1r_2_16_scn4m_subm U0 (.DIN0(din0), + .DOUT0(dout0), + .ADDR0(addr0), + .csb0(csb0), + .web0(web0), + .clk0(clk), + .DOUT1(dout1), + .ADDR1(addr1), + .csb1(csb1), + .clk1(clk) + ); + + + initial + begin + + //$monitor("%g addr0=%b din0=%b dout0=%b addr1=%b dout1=%b", + // $time, addr0, din0, dout0, addr1, dout1); + + clk = 1; + csb0 = 1; + web0 = 1; + addr0 = 0; + din0 = 0; + + csb1 = 1; + addr1 = 0; + + // write + #10 din0=2'b10; + addr0=4'h1; + web0 = 0; + csb0 = 0; + // nop + csb1 = 1; + addr1 = 0; + + // write another + #10 din0=2'b01; + addr0=4'hC; + web0 = 0; + csb0 = 0; + // read last + csb1 = 0; + addr1 = 4'h1; + + #10 `assert(dout1, 2'b10) + + // read undefined + din0=2'b11; + addr0=4'h0; + web0 = 1; + csb0 = 0; + // read another + csb1 = 0; + addr1 = 4'hC; + + #10 `assert(dout0, 2'bxx) + `assert(dout1, 2'b01) + + // read defined + din0=2'b11; + addr0=4'hC; + web0 = 1; + csb0 = 0; + // read undefined + csb1 = 0; + addr1 = 4'hD; + + #10 `assert(dout0, 2'b01) + `assert(dout1, 2'bxx) + + // write another + din0=2'b11; + addr0=4'hA; + web0 = 0; + csb0 = 0; + // read the feedthrough value + csb1 = 0; + addr1 = 4'hA; + + #10 `assert(dout1, 2'b11) + + // read defined + din0=2'b11; + addr0=4'h1; + web0 = 1; + csb0 = 0; + // read old value + csb1 = 0; + addr1 = 4'h1; + + #10 `assert(dout0, 2'b10) + `assert(dout1, 2'b10) + + // read defined + din0=2'b11; + addr0=4'hA; + web0 = 1; + csb0 = 0; + // dual read + csb1 = 0; + addr1 = 4'hA; + #10 `assert(dout0, 2'b11) + `assert(dout1, 2'b11) + + // read undefined + din0=2'b11; + addr0=4'h0; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'bxx) + + #10 $finish; + + end + + always + #5 clk = !clk; + +endmodule diff --git a/compiler/tests/sram_1rw_tb.v b/compiler/tests/sram_1rw_tb.v new file mode 100644 index 00000000..31f120e8 --- /dev/null +++ b/compiler/tests/sram_1rw_tb.v @@ -0,0 +1,103 @@ +`define assert(signal, value) \ +if (!(signal === value)) begin \ + $display("ASSERTION FAILED in %m: signal != value"); \ + $finish;\ +end + +module sram_1rw_tb; + reg clk; + + reg [3:0] addr0; + reg [1:0] din0; + reg csb0; + reg web0; + wire [1:0] dout0; + + sram_2_16_scn4m_subm U0 (.DIN0(din0), + .DOUT0(dout0), + .ADDR0(addr0), + .csb0(csb0), + .web0(web0), + .clk0(clk) + ); + + + initial + begin + + //$monitor("%g addr0=%b din0=%b dout0=%b", + // $time, addr0, din0, dout0); + + + clk = 1; + csb0 = 1; + web0 = 1; + addr0 = 0; + din0 = 0; + + // write + #10 din0=2'b10; + addr0=4'h1; + web0 = 0; + csb0 = 0; + + // write another + #10 din0=2'b01; + addr0=4'hC; + web0 = 0; + csb0 = 0; + + // read undefined + #10 din0=2'b11; + addr0=4'h0; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'bxx) + + // read defined + din0=2'b11; + addr0=4'hC; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'b01) + + // write another + din0=2'b11; + addr0=4'hA; + web0 = 0; + csb0 = 0; + + // read defined + #10 din0=2'b11; + addr0=4'h1; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'b10) + + // read defined + din0=2'b11; + addr0=4'hA; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'b11) + + // read undefined + din0=2'b11; + addr0=4'h0; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'bxx) + + #10 $finish; + + end + + always + #5 clk = !clk; + +endmodule diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py old mode 100644 new mode 100755 index 79333318..6b2a7dcf --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -1,12 +1,14 @@ import unittest,warnings import sys,os,glob,copy +import shutil sys.path.append(os.path.join(sys.path[0],"..")) from globals import OPTS import debug class openram_test(unittest.TestCase): """ Base unit test that we have some shared classes in. """ - + + def local_drc_check(self, w): self.reset() @@ -29,21 +31,40 @@ class openram_test(unittest.TestCase): tempgds = OPTS.openram_temp + "temp.gds" a.sp_write(tempspice) - a.gds_write(tempgds) + # cannot write gds in netlist_only mode + if not OPTS.netlist_only: + a.gds_write(tempgds) - import verify - result=verify.run_drc(a.name, tempgds) - if result != 0: - self.fail("DRC failed: {}".format(a.name)) + import verify + result=verify.run_drc(a.name, tempgds) + if result != 0: + #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) + #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) + #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) + self.fail("DRC failed: {}".format(a.name)) - result=verify.run_lvs(a.name, tempgds, tempspice, final_verification) - if result != 0: - self.fail("LVS mismatch: {}".format(a.name)) + result=verify.run_lvs(a.name, tempgds, tempspice, final_verification) + if result != 0: + #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) + #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) + #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) + self.fail("LVS mismatch: {}".format(a.name)) if OPTS.purge_temp: self.cleanup() + def find_feasible_test_period(self, delay_obj, sram, load, slew): + """Creates a delay simulation to determine a feasible period for the functional tests to run. + Only determines the feasible period for a single port and assumes that for all ports for performance. + """ + debug.info(1, "Finding feasible period for current test.") + delay_obj.set_load_slew(load, slew) + delay_obj.set_probe(probe_address="1"*sram.addr_size, probe_data=(sram.word_size-1)) + test_port = delay_obj.read_ports[0] #Only test one port, assumes other ports have similar period. + delay_obj.find_feasible_period_one_port(test_port) + return delay_obj.period + def cleanup(self): """ Reset the duplicate checker and cleanup files. """ files = glob.glob(OPTS.openram_temp + '*') @@ -75,7 +96,8 @@ class openram_test(unittest.TestCase): 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 self.isclose(k,data[k],golden_data[k],error_tolerance): + data_matches = False if not data_matches: import pprint data_string=pprint.pformat(data) diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index 4cc3a093..7510b340 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -70,7 +70,7 @@ num_drc_runs = 0 num_lvs_runs = 0 num_pex_runs = 0 -def run_drc(cell_name, gds_name): +def run_drc(cell_name, gds_name, extract=False, final_verification=False): """Run DRC check on a given top-level name which is implemented in gds_name.""" @@ -175,18 +175,21 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): 'cmnFDIUseLayerMap': 1, 'cmnTranscriptFile': './lvs.log', 'cmnTranscriptEchoToFile': 1, - 'lvsRecognizeGates': 'NONE', - # FIXME: Remove when vdd/gnd connected - 'cmnVConnectNamesState' : 'ALL', #connects all nets with the same namee - # FIXME: Remove when vdd/gnd connected - 'lvsAbortOnSupplyError' : 0 + 'lvsRecognizeGates': 'NONE', } + # FIXME: Remove when vdd/gnd connected + #'cmnVConnectNamesState' : 'ALL', #connects all nets with the same namee + # FIXME: Remove when vdd/gnd connected + #'lvsAbortOnSupplyError' : 0 # This should be removed for final verification if not final_verification: lvs_runset['cmnVConnectReport']=1 lvs_runset['cmnVConnectNamesState']='SOME' lvs_runset['cmnVConnectNames']='vdd gnd' + else: + lvs_runset['lvsAbortOnSupplyError']=1 + # write the runset file diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 55e803b4..490a09a1 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -26,7 +26,7 @@ num_drc_runs = 0 num_lvs_runs = 0 num_pex_runs = 0 -def write_magic_script(cell_name, gds_name, extract=False): +def write_magic_script(cell_name, gds_name, extract=False, final_verification=False): """ Write a magic script to perform DRC and optionally extraction. """ global OPTS @@ -41,23 +41,28 @@ def write_magic_script(cell_name, gds_name, extract=False): f.write("load {}\n".format(cell_name)) # Flatten the cell to get rid of DRCs spanning multiple layers # (e.g. with routes) - f.write("flatten {}_new\n".format(cell_name)) - f.write("load {}_new\n".format(cell_name)) - f.write("cellname rename {0}_new {0}\n".format(cell_name)) - f.write("load {}\n".format(cell_name)) + #f.write("flatten {}_new\n".format(cell_name)) + #f.write("load {}_new\n".format(cell_name)) + #f.write("cellname rename {0}_new {0}\n".format(cell_name)) + #f.write("load {}\n".format(cell_name)) f.write("writeall force\n") f.write("drc check\n") f.write("drc catchup\n") f.write("drc count total\n") f.write("drc count\n") - if extract: - f.write("extract all\n") - f.write("ext2spice hierarchy on\n") - f.write("ext2spice scale off\n") - # Can choose hspice, ngspice, or spice3, - # but they all seem compatible enough. - #f.write("ext2spice format ngspice\n") - f.write("ext2spice\n") + if not extract: + pre = "#" + else: + pre = "" + if final_verification: + f.write(pre+"extract unique\n") + f.write(pre+"extract\n") + f.write(pre+"ext2spice hierarchy on\n") + f.write(pre+"ext2spice scale off\n") + # Can choose hspice, ngspice, or spice3, + # but they all seem compatible enough. + #f.write(pre+"ext2spice format ngspice\n") + f.write(pre+"ext2spice\n") f.write("quit -noprompt\n") f.write("EOF\n") @@ -68,11 +73,13 @@ def write_netgen_script(cell_name, sp_name): """ Write a netgen script to perform LVS. """ global OPTS - # This is a hack to prevent netgen from re-initializing the LVS - # commands. It will be unnecessary after Tim adds the nosetup option. - setup_file = OPTS.openram_temp + "setup.tcl" - f = open(setup_file, "w") - f.close() + + setup_file = OPTS.openram_tech + "mag_lib/setup.tcl" + if os.path.exists(setup_file): + # Copy setup.tcl file into temp dir + shutil.copy(setup_file, OPTS.openram_temp) + else: + setup_file = 'nosetup' run_file = OPTS.openram_temp + "run_lvs.sh" f = open(run_file, "w") @@ -86,44 +93,27 @@ def write_netgen_script(cell_name, sp_name): # cell_name)) # f.write("property {{{0}{1}.spice pfet}} tolerance {{w 0.1}}\n".format(OPTS.openram_temp, # cell_name)) - f.write("lvs {0}.spice {{{1} {0}}} setup.tcl {0}.lvs.report\n".format(cell_name, sp_name)) + f.write("lvs {0}.spice {{{1} {0}}} {2} {0}.lvs.report\n".format(cell_name, sp_name, setup_file)) f.write("quit\n") f.write("EOF\n") f.close() os.system("chmod u+x {}".format(run_file)) - setup_file = OPTS.openram_temp + "setup.tcl" - f = open(setup_file, "w") - f.write("ignore class c\n") - f.write("equate class {{nfet {0}.spice}} {{n {1}}}\n".format(cell_name, sp_name)) - f.write("equate class {{pfet {0}.spice}} {{p {1}}}\n".format(cell_name, sp_name)) - # This circuit has symmetries and needs to be flattened to resolve them or the banks won't pass - # Is there a more elegant way to add this when needed? - f.write("flatten class {{{0}.spice precharge_array_1}}\n".format(cell_name)) - f.write("flatten class {{{0}.spice precharge_array_2}}\n".format(cell_name)) - f.write("flatten class {{{0}.spice precharge_array_3}}\n".format(cell_name)) - f.write("flatten class {{{0}.spice precharge_array_4}}\n".format(cell_name)) - f.write("property {{nfet {0}.spice}} remove as ad ps pd\n".format(cell_name)) - f.write("property {{pfet {0}.spice}} remove as ad ps pd\n".format(cell_name)) - f.write("property {{n {0}}} remove as ad ps pd\n".format(sp_name)) - f.write("property {{p {0}}} remove as ad ps pd\n".format(sp_name)) - f.write("permute transistors\n") - f.write("permute pins n source drain\n") - f.write("permute pins p source drain\n") - f.close() - -def run_drc(cell_name, gds_name, extract=False): +def run_drc(cell_name, gds_name, extract=True, final_verification=False): """Run DRC check on a cell which is implemented in gds_name.""" global num_drc_runs num_drc_runs += 1 # Copy .magicrc file into temp dir - shutil.copy(OPTS.openram_tech + "/mag_lib/.magicrc", - OPTS.openram_temp) + magic_file = OPTS.openram_tech + "mag_lib/.magicrc" + if os.path.exists(magic_file): + shutil.copy(magic_file, OPTS.openram_temp) + else: + debug.warning("Could not locate .magicrc file: {}".format(magic_file)) - write_magic_script(cell_name, gds_name, extract) + write_magic_script(cell_name, gds_name, extract, final_verification) # run drc cwd = os.getcwd() @@ -176,7 +166,6 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): global num_lvs_runs num_lvs_runs += 1 - run_drc(cell_name, gds_name, extract=True) write_netgen_script(cell_name, sp_name) # run LVS diff --git a/compiler/view_profile.py b/compiler/view_profile.py new file mode 100755 index 00000000..5a522b8a --- /dev/null +++ b/compiler/view_profile.py @@ -0,0 +1,6 @@ +import pstats +p = pstats.Stats(‘profile.dat’) +p.strip_dirs() +p.sort_stats(‘cumulative’) +p.print_stats(50) + diff --git a/docs/figs/sense_amp_schem.pdf b/docs/figs/sense_amp_schem.pdf index 0b062ffe..01c202be 100644 Binary files a/docs/figs/sense_amp_schem.pdf and b/docs/figs/sense_amp_schem.pdf differ diff --git a/docs/figs/sense_amp_schem.svg b/docs/figs/sense_amp_schem.svg index 749b6803..946aac45 100644 --- a/docs/figs/sense_amp_schem.svg +++ b/docs/figs/sense_amp_schem.svg @@ -1,568 +1,113 @@ - - - - - - - - - - - image/svg+xml - - - - + + + Produced by OmniGraffle 7.6.1 + 2018-11-16 00:52:28 +0000 - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - vdd - DATA - br - bl - en - en - en - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + Canvas 1 + + + Layer 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vdd + + + DA + T + A + + + br + + + bl + + + en + + + en + + + en + + + + + + + + + + + + + + + + + diff --git a/technology/freepdk45/gds_lib/cell_1rw_1r.gds b/technology/freepdk45/gds_lib/cell_1rw_1r.gds new file mode 100644 index 00000000..5f3e7213 Binary files /dev/null and b/technology/freepdk45/gds_lib/cell_1rw_1r.gds differ diff --git a/technology/freepdk45/gds_lib/cell_6t.gds b/technology/freepdk45/gds_lib/cell_6t.gds index bde8742a..17b62028 100644 Binary files a/technology/freepdk45/gds_lib/cell_6t.gds and b/technology/freepdk45/gds_lib/cell_6t.gds differ diff --git a/technology/freepdk45/gds_lib/replica_cell_1rw_1r.gds b/technology/freepdk45/gds_lib/replica_cell_1rw_1r.gds new file mode 100644 index 00000000..cdb099ac Binary files /dev/null and b/technology/freepdk45/gds_lib/replica_cell_1rw_1r.gds differ diff --git a/technology/freepdk45/gds_lib/replica_cell_6t.gds b/technology/freepdk45/gds_lib/replica_cell_6t.gds index 2881a883..f2fe9197 100644 Binary files a/technology/freepdk45/gds_lib/replica_cell_6t.gds and b/technology/freepdk45/gds_lib/replica_cell_6t.gds differ diff --git a/technology/freepdk45/gds_lib/sense_amp.gds b/technology/freepdk45/gds_lib/sense_amp.gds index c215f793..fecbfcb8 100644 Binary files a/technology/freepdk45/gds_lib/sense_amp.gds and b/technology/freepdk45/gds_lib/sense_amp.gds differ diff --git a/technology/freepdk45/gds_lib/write_driver.gds b/technology/freepdk45/gds_lib/write_driver.gds index 2c555685..742e39d4 100644 Binary files a/technology/freepdk45/gds_lib/write_driver.gds and b/technology/freepdk45/gds_lib/write_driver.gds differ diff --git a/technology/freepdk45/sp_lib/cell_1rw_1r.sp b/technology/freepdk45/sp_lib/cell_1rw_1r.sp new file mode 100644 index 00000000..f1c45604 --- /dev/null +++ b/technology/freepdk45/sp_lib/cell_1rw_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT cell_1rw_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1 gnd NMOS_VTG W=180.0n L=50n m=1 +MM8 RA_to_R_right Q gnd gnd NMOS_VTG W=180.0n L=50n m=1 +MM7 RA_to_R_left Q_bar gnd gnd NMOS_VTG W=180.0n L=50n m=1 +MM6 RA_to_R_left wl1 bl1 gnd NMOS_VTG W=180.0n L=50n m=1 +MM5 Q wl0 bl0 gnd NMOS_VTG W=135.00n L=50n m=1 +MM4 Q_bar wl0 br0 gnd NMOS_VTG W=135.00n L=50n m=1 +MM1 Q Q_bar gnd gnd NMOS_VTG W=205.0n L=50n m=1 +MM0 Q_bar Q gnd gnd NMOS_VTG W=205.0n L=50n m=1 +MM3 Q Q_bar vdd vdd PMOS_VTG W=90n L=50n m=1 +MM2 Q_bar Q vdd vdd PMOS_VTG W=90n L=50n m=1 +.ENDS + diff --git a/technology/freepdk45/sp_lib/cell_6t.sp b/technology/freepdk45/sp_lib/cell_6t.sp index cb9cbc3c..e1e4936d 100644 --- a/technology/freepdk45/sp_lib/cell_6t.sp +++ b/technology/freepdk45/sp_lib/cell_6t.sp @@ -1,10 +1,15 @@ .SUBCKT cell_6t bl br wl vdd gnd +* Inverter 1 +MM0 Qbar Q gnd gnd NMOS_VTG W=205.00n L=50n +MM4 Qbar Q vdd vdd PMOS_VTG W=90n L=50n + +* Inverer 2 +MM1 Q Qbar gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q Qbar vdd vdd PMOS_VTG W=90n L=50n + +* Access transistors 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 +MM2 br wl Qbar gnd NMOS_VTG W=135.00n L=50n .ENDS cell_6t diff --git a/technology/freepdk45/sp_lib/replica_cell_1rw_1r.sp b/technology/freepdk45/sp_lib/replica_cell_1rw_1r.sp new file mode 100644 index 00000000..d108b7bf --- /dev/null +++ b/technology/freepdk45/sp_lib/replica_cell_1rw_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT replica_cell_1rw_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1 gnd NMOS_VTG W=180.0n L=50n m=1 +MM8 RA_to_R_right Q gnd gnd NMOS_VTG W=180.0n L=50n m=1 +MM7 RA_to_R_left vdd gnd gnd NMOS_VTG W=180.0n L=50n m=1 +MM6 RA_to_R_left wl1 bl1 gnd NMOS_VTG W=180.0n L=50n m=1 +MM5 Q wl0 bl0 gnd NMOS_VTG W=135.00n L=50n m=1 +MM4 vdd wl0 br0 gnd NMOS_VTG W=135.00n L=50n m=1 +MM1 Q vdd gnd gnd NMOS_VTG W=205.0n L=50n m=1 +MM0 vdd Q gnd gnd NMOS_VTG W=205.0n L=50n m=1 +MM3 Q vdd vdd vdd PMOS_VTG W=90n L=50n m=1 +MM2 vdd Q vdd vdd PMOS_VTG W=90n L=50n m=1 +.ENDS + diff --git a/technology/freepdk45/sp_lib/replica_cell_6t.sp b/technology/freepdk45/sp_lib/replica_cell_6t.sp index 3a7c40dd..dd29028a 100644 --- a/technology/freepdk45/sp_lib/replica_cell_6t.sp +++ b/technology/freepdk45/sp_lib/replica_cell_6t.sp @@ -1,10 +1,15 @@ .SUBCKT replica_cell_6t bl br wl vdd gnd -MM3 bl wl gnd gnd NMOS_VTG W=135.00n L=50n -MM2 br wl net4 gnd NMOS_VTG W=135.00n L=50n -MM1 gnd net4 gnd gnd NMOS_VTG W=205.00n L=50n -MM0 net4 gnd gnd gnd NMOS_VTG W=205.00n L=50n -MM5 gnd net4 vdd vdd PMOS_VTG W=90n L=50n -MM4 net4 gnd vdd vdd PMOS_VTG W=90n L=50n -.ENDS replica_cell_6t +* Inverter 1 +MM0 vdd Q gnd gnd NMOS_VTG W=205.00n L=50n +MM4 vdd Q vdd vdd PMOS_VTG W=90n L=50n + +* Inverer 2 +MM1 Q vdd gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q vdd vdd vdd PMOS_VTG W=90n L=50n + +* Access transistors +MM3 bl wl Q gnd NMOS_VTG W=135.00n L=50n +MM2 br wl vdd gnd NMOS_VTG W=135.00n L=50n +.ENDS cell_6t diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 74bef19c..14ce1953 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -1,15 +1,10 @@ import os +from design_rules import * """ File containing the process technology parameters for FreePDK 45nm. """ -info = {} -info["name"] = "freepdk45" -info["body_tie_down"] = 0 -info["has_pwell"] = True -info["has_nwell"] = True - #GDS file info GDS = {} # gds units @@ -71,8 +66,18 @@ parameter={} parameter["min_tx_size"] = 0.09 parameter["beta"] = 3 +parameter["6T_inv_nmos_size"] = 0.205 +parameter["6T_inv_pmos_size"] = 0.09 +parameter["6T_access_size"] = 0.135 + drclvs_home=os.environ.get("DRCLVS_HOME") -drc={} + +drc = design_rules("freepdk45") + +drc["body_tie_down"] = 0 +drc["has_pwell"] = True +drc["has_nwell"] = True + #grid size drc["grid"] = 0.0025 @@ -83,7 +88,7 @@ drc["xrc_rules"]=drclvs_home+"/calibrexRC.rul" drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/freepdk45/layers.map" # minwidth_tx with contact (no dog bone transistors) -drc["minwidth_tx"]=0.09 +drc["minwidth_tx"] = 0.09 drc["minlength_channel"] = 0.05 # WELL.2 Minimum spacing of nwell/pwell at different potential @@ -196,7 +201,18 @@ drc["via2_to_via2"] = 0.075 # METALINT.1 Minimum width of intermediate metal drc["minwidth_metal3"] = 0.07 # METALINT.2 Minimum spacing of intermediate metal -drc["metal3_to_metal3"] = 0.07 +#drc["metal3_to_metal3"] = 0.07 +# Minimum spacing of metal3 wider than 0.09 & longer than 0.3 = 0.09 +# Minimum spacing of metal3 wider than 0.27 & longer than 0.9 = 0.27 +# Minimum spacing of metal3 wider than 0.5 & longer than 1.8 = 0.5 +# Minimum spacing of metal3 wider than 0.9 & longer than 2.7 = 0.9 +# Minimum spacing of metal3 wider than 1.5 & longer than 4.0 = 1.5 +drc["metal3_to_metal3"] = drc_lut({(0.00, 0.0) : 0.07, + (0.09, 0.3) : 0.09, + (0.27, 0.9) : 0.27, + (0.50, 1.8) : 0.5, + (0.90, 2.7) : 0.9, + (1.50, 4.0) : 1.5}) # METALINT.3 Minimum enclosure around via1 on two opposite sides drc["metal3_extend_via2"] = 0.035 # Reserved for asymmetric enclosures @@ -209,22 +225,29 @@ drc["metal3_enclosure_via3"] = 0 drc["minarea_metal3"] = 0 # VIA2-3.1 Minimum width of Via[2-3] -drc["minwidth_via3"] = 0.065 +drc["minwidth_via3"] = 0.07 # VIA2-3.2 Minimum spacing of Via[2-3] -drc["via3_to_via3"] = 0.07 +drc["via3_to_via3"] = 0.085 # METALSMG.1 Minimum width of semi-global metal drc["minwidth_metal4"] = 0.14 # METALSMG.2 Minimum spacing of semi-global metal -drc["metal4_to_metal4"] = 0.14 +#drc["metal4_to_metal4"] = 0.14 +# Minimum spacing of metal4 wider than 0.27 & longer than 0.9 = 0.27 +# Minimum spacing of metal4 wider than 0.5 & longer than 1.8 = 0.5 +# Minimum spacing of metal4 wider than 0.9 & longer than 2.7 = 0.9 +# Minimum spacing of metal4 wider than 1.5 & longer than 4.0 = 1.5 +drc["metal4_to_metal4"] = drc_lut({(0.00, 0.0) : 0.14, + (0.27, 0.9) : 0.27, + (0.50, 1.8) : 0.5, + (0.90, 2.7) : 0.9, + (1.50, 4.0) : 1.5}) # METALSMG.3 Minimum enclosure around via[3-6] on two opposite sides -drc["metal4_extend_via3"] = 0.07 +drc["metal4_extend_via3"] = 0.0025 # Reserved for asymmetric enclosure -drc["metal4_enclosure_via3"] = 0 -# METALSMG.3 Minimum enclosure around via[3-6] on two opposite sides -drc["metal4_enclosure_via4"] = 0 -# Reserved for asymmetric enclosure -drc["metal4_extend_via4"] = 0.07 +drc["metal4_enclosure_via3"] = 0.0025 +# Not a rule +drc["minarea_metal4"] = 0 # Metal 5-10 are ommitted @@ -272,6 +295,7 @@ spice["channel"] = drc["minlength_channel"] spice["clk"] = "clk" # analytical delay parameters +spice["v_threshold_typical"] = 0.4 # Typical Threshold voltage in Volts spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2 spice["min_tx_r"] = 9250.0 # Minimum transistor on resistance in ohms @@ -304,6 +328,13 @@ spice["nand2_transition_prob"] = .1875 # Transition probability of 2-input na spice["nand3_transition_prob"] = .1094 # Transition probability of 3-input nand. spice["nor2_transition_prob"] = .1875 # Transition probability of 2-input nor. +#Logical Effort relative values for the Handmade cells +parameter["dff_clk_cin"] = 30.6 #relative capacitance +parameter["6tcell_wl_cin"] = 3 #relative capacitance +parameter["min_inv_para_delay"] = .5 #Tau delay units +parameter["sa_en_pmos_size"] = .72 #micro-meters +parameter["sa_en_nmos_size"] = .27 #micro-meters + ################################################### ##END Spice Simulation Parameters ################################################### diff --git a/technology/freepdk45/tf/FreePDK45.tf b/technology/freepdk45/tf/FreePDK45.tf index 0021e644..f0bfc606 100644 --- a/technology/freepdk45/tf/FreePDK45.tf +++ b/technology/freepdk45/tf/FreePDK45.tf @@ -172,9 +172,9 @@ layerDefinitions( ( align drawing ) ( hardFence drawing ) ( softFence drawing ) - ( text drawing ) - ( text drawing1 ) - ( text drawing2 ) + ( comment drawing ) + ( comment drawing1 ) + ( comment drawing2 ) ( border drawing ) ( device drawing ) ( device label ) @@ -379,9 +379,9 @@ layerDefinitions( ( align drawing align t t t t nil ) ( hardFence drawing hardFence t t t t nil ) ( softFence drawing softFence t t t t nil ) - ( text drawing text t t t t t ) - ( text drawing1 text1 t t t t nil ) - ( text drawing2 text2 t t t t nil ) + ( comment drawing comment t t t t t ) + ( comment drawing1 comment1 t t t t nil ) + ( comment drawing2 comment2 t t t t nil ) ( border drawing border t t t t nil ) ( device drawing device t t t t nil ) ( device label deviceLbl t t t t nil ) diff --git a/technology/freepdk45/tf/layers.map b/technology/freepdk45/tf/layers.map index 8c0c18d2..1bc4d413 100644 --- a/technology/freepdk45/tf/layers.map +++ b/technology/freepdk45/tf/layers.map @@ -27,4 +27,4 @@ via8 drawing 26 0 metal9 drawing 27 0 via9 drawing 28 0 metal10 drawing 29 0 -text drawing 239 0 +comment drawing 239 0 diff --git a/technology/scn3me_subm/mag_lib/setup.tcl b/technology/scn3me_subm/mag_lib/setup.tcl new file mode 100644 index 00000000..af55a416 --- /dev/null +++ b/technology/scn3me_subm/mag_lib/setup.tcl @@ -0,0 +1,15 @@ +# Setup file for netgen +ignore class c +equate class {-circuit1 nfet} {-circuit2 n} +equate class {-circuit1 pfet} {-circuit2 p} +# This circuit has symmetries and needs to be flattened to resolve them +# or the banks won't pass +flatten class {-circuit1 precharge_array_1} +flatten class {-circuit1 precharge_array_2} +flatten class {-circuit1 precharge_array_3} +flatten class {-circuit1 precharge_array_4} +property {-circuit1 nfet} remove as ad ps pd +property {-circuit1 pfet} remove as ad ps pd +property {-circuit2 n} remove as ad ps pd +property {-circuit2 p} remove as ad ps pd +permute transistors diff --git a/technology/scn3me_subm/sp_lib/write_driver.sp b/technology/scn3me_subm/sp_lib/write_driver.sp index edddf18c..88f80361 100644 --- a/technology/scn3me_subm/sp_lib/write_driver.sp +++ b/technology/scn3me_subm/sp_lib/write_driver.sp @@ -2,34 +2,35 @@ .SUBCKT write_driver din bl br en vdd gnd **** Inverter to conver Data_in to data_in_bar ****** -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 +* din_bar = inv(din) +M_1 din_bar din gnd gnd n W=1.2u L=0.6u +M_2 din_bar din vdd vdd p W=2.1u L=0.6u **** 2input nand gate follwed by inverter to drive BL ****** -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 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 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 +* din_bar_gated = nand(en, din) +M_3 din_bar_gated en net_7 gnd n W=2.1u L=0.6u +M_4 net_7 din gnd gnd n W=2.1u L=0.6u +M_5 din_bar_gated en vdd vdd p W=2.1u L=0.6u +M_6 din_bar_gated din vdd vdd p W=2.1u L=0.6u +* din_bar_gated_bar = inv(din_bar_gated) +M_7 din_bar_gated_bar din_bar_gated vdd vdd p W=2.1u L=0.6u +M_8 din_bar_gated_bar din_bar_gated gnd gnd n W=1.2u L=0.6u **** 2input nand gate follwed by inverter to drive BR****** - -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 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 +* din_gated = nand(en, din_bar) +M_9 din_gated en vdd vdd p W=2.1u L=0.6u +M_10 din_gated en net_8 gnd n W=2.1u L=0.6u +M_11 net_8 din_bar gnd gnd n W=2.1u L=0.6u +M_12 din_gated din_bar vdd vdd p W=2.1u L=0.6u +* din_gated_bar = inv(din_gated) +M_13 din_gated_bar din_gated vdd vdd p W=2.1u L=0.6u +M_14 din_gated_bar din_gated gnd gnd n W=1.2u L=0.6u ************************************************ - -M_15 bl net_6 net_5 gnd n W='3.6*1u' L=0.6u -M_16 br net_1 net_5 gnd n W='3.6*1u' L=0.6u -M_17 net_5 en gnd gnd n W='3.6*1u' L=0.6u +* pull down with en enable +M_15 bl din_gated_bar net_5 gnd n W=3.6u L=0.6u +M_16 br din_bar_gated_bar net_5 gnd n W=3.6u L=0.6u +M_17 net_5 en gnd gnd n W=3.6u L=0.6u diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index e6bf6da1..d448b5dc 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -1,15 +1,10 @@ import os +from design_rules import * """ File containing the process technology parameters for SCMOS 3me, subm, 180nm. """ -info={} -info["name"]="scn3me_subm" -info["body_tie_down"] = 0 -info["has_pwell"] = True -info["has_nwell"] = True - #GDS file info GDS={} # gds units @@ -57,9 +52,19 @@ parameter={} parameter["min_tx_size"] = 4*_lambda_ parameter["beta"] = 2 +parameter["6T_inv_nmos_size"] = 8*_lambda_ +parameter["6T_inv_pmos_size"] = 3*_lambda_ +parameter["6T_access_size"] = 4*_lambda_ + drclvs_home=os.environ.get("DRCLVS_HOME") -drc={} +drc = design_rules("scn3me_subm") + +drc["body_tie_down"] = 0 +drc["has_pwell"] = True +drc["has_nwell"] = True + + #grid size is 1/2 a lambda drc["grid"]=0.5*_lambda_ #DRC/LVS test set_up @@ -235,6 +240,7 @@ spice["clk"] = "clk" # analytical delay parameters # FIXME: These need to be updated for SCMOS, they are copied from FreePDK45. +spice["v_threshold_typical"] = 1.3 # Typical Threshold voltage in Volts spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2 spice["min_tx_r"] = 9250.0 # Minimum transistor on resistance in ohms diff --git a/technology/scn3me_subm/tf/display.drf b/technology/scn3me_subm/tf/display.drf index 047879b6..4bd251e8 100644 --- a/technology/scn3me_subm/tf/display.drf +++ b/technology/scn3me_subm/tf/display.drf @@ -625,7 +625,7 @@ drDefinePacket( ( display deviceAnt stipple0 solid yellow yellow solid ) ( display winBottomShadow solid solid winColor1 winColor1 solid ) ( display PselectNet dots4 solid brown brown outlineStipple) - ( display comment stipple0 lineStyle0 winBack winBack outline ) + ( display comment stipple0 lineStyle0 winBack winBack outlineStipple) ( display Poly1 dots lineStyle0 red red outlineStipple) ( display Unrouted stipple0 lineStyle1 winColor5 winColor5 solid ) ( display stretch stipple0 solid yellow yellow solid ) diff --git a/technology/scn4m_subm/gds_lib/cell_1rw_1r.gds b/technology/scn4m_subm/gds_lib/cell_1rw_1r.gds new file mode 100644 index 00000000..71a6dfad Binary files /dev/null and b/technology/scn4m_subm/gds_lib/cell_1rw_1r.gds differ diff --git a/technology/scn4m_subm/gds_lib/replica_cell_1rw_1r.gds b/technology/scn4m_subm/gds_lib/replica_cell_1rw_1r.gds new file mode 100644 index 00000000..2697651d Binary files /dev/null and b/technology/scn4m_subm/gds_lib/replica_cell_1rw_1r.gds differ diff --git a/technology/scn4m_subm/gds_lib/replica_cell_6t.gds b/technology/scn4m_subm/gds_lib/replica_cell_6t.gds index 6a6b32ad..f16f7b13 100644 Binary files a/technology/scn4m_subm/gds_lib/replica_cell_6t.gds and b/technology/scn4m_subm/gds_lib/replica_cell_6t.gds differ diff --git a/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag b/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag new file mode 100644 index 00000000..9aec1c5d --- /dev/null +++ b/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag @@ -0,0 +1,142 @@ +magic +tech scmos +timestamp 1542220294 +<< nwell >> +rect 0 46 54 75 +<< pwell >> +rect 0 0 54 46 +<< ntransistor >> +rect 14 33 16 37 +rect 22 29 24 37 +rect 30 29 32 37 +rect 38 33 40 37 +rect 14 17 16 23 +rect 22 17 24 23 +rect 30 17 32 23 +rect 38 17 40 23 +<< ptransistor >> +rect 22 54 24 57 +rect 30 54 32 57 +<< ndiffusion >> +rect 13 33 14 37 +rect 16 33 17 37 +rect 21 33 22 37 +rect 17 29 22 33 +rect 24 29 25 37 +rect 29 29 30 37 +rect 32 33 33 37 +rect 37 33 38 37 +rect 40 33 41 37 +rect 32 29 37 33 +rect 9 21 14 23 +rect 13 17 14 21 +rect 16 17 22 23 +rect 24 17 25 23 +rect 29 17 30 23 +rect 32 17 38 23 +rect 40 21 45 23 +rect 40 17 41 21 +<< pdiffusion >> +rect 21 54 22 57 +rect 24 54 25 57 +rect 29 54 30 57 +rect 32 54 33 57 +<< ndcontact >> +rect 9 33 13 37 +rect 17 33 21 37 +rect 25 29 29 37 +rect 33 33 37 37 +rect 41 33 45 37 +rect 25 17 29 23 +<< pdcontact >> +rect 17 54 21 58 +rect 33 54 37 58 +<< psubstratepcontact >> +rect 25 9 29 13 +<< polysilicon >> +rect 22 57 24 60 +rect 30 57 32 60 +rect 22 44 24 54 +rect 30 51 32 54 +rect 31 47 32 51 +rect 14 37 16 44 +rect 22 40 23 44 +rect 22 37 24 40 +rect 30 37 32 47 +rect 38 37 40 44 +rect 14 31 16 33 +rect 38 31 40 33 +rect 14 23 16 24 +rect 22 23 24 29 +rect 30 23 32 29 +rect 38 23 40 24 +rect 14 15 16 17 +rect 22 15 24 17 +rect 30 15 32 17 +rect 38 15 40 17 +<< polycontact >> +rect 27 47 31 51 +rect 10 40 14 44 +rect 23 40 27 44 +rect 40 40 44 44 +rect 12 24 16 28 +rect 38 24 42 28 +<< metal1 >> +rect 0 68 25 72 +rect 29 68 54 72 +rect 0 61 54 65 +rect 10 44 14 61 +rect 17 51 20 54 +rect 17 47 27 51 +rect 17 37 20 47 +rect 34 44 37 54 +rect 27 40 37 44 +rect 40 44 44 61 +rect 34 37 37 40 +rect 6 33 9 37 +rect 45 33 48 37 +rect 25 23 29 29 +rect 25 13 29 17 +rect 0 9 25 13 +rect 29 9 54 13 +rect 0 2 16 6 +rect 20 2 34 6 +rect 38 2 54 6 +<< m2contact >> +rect 2 33 6 37 +rect 48 33 52 37 +rect 16 24 20 28 +rect 34 24 38 28 +rect 16 2 20 6 +rect 34 2 38 6 +<< pdm12contact >> +rect 25 54 29 58 +<< ndm12contact >> +rect 9 17 13 21 +rect 41 17 45 21 +<< nsm12contact >> +rect 25 68 29 72 +<< metal2 >> +rect 2 37 6 72 +rect 2 0 6 33 +rect 9 21 13 72 +rect 25 58 29 68 +rect 9 0 13 17 +rect 16 6 20 24 +rect 34 6 38 24 +rect 41 21 45 72 +rect 41 0 45 17 +rect 48 37 52 72 +rect 48 0 52 33 +<< comment >> +rect 0 0 54 70 +<< labels >> +rlabel metal1 19 63 19 63 1 wl0 +rlabel metal1 19 70 19 70 5 vdd +rlabel metal1 27 4 27 4 1 wl1 +rlabel psubstratepcontact 27 11 27 11 1 gnd +rlabel metal2 4 7 4 7 2 bl0 +rlabel metal2 11 7 11 7 1 bl1 +rlabel metal2 43 7 43 7 1 br1 +rlabel metal2 50 7 50 7 8 br0 +<< end >> diff --git a/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag b/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag new file mode 100644 index 00000000..f215ff04 --- /dev/null +++ b/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag @@ -0,0 +1,145 @@ +magic +tech scmos +timestamp 1542221056 +<< nwell >> +rect 0 46 54 75 +<< pwell >> +rect 0 0 54 46 +<< ntransistor >> +rect 14 33 16 37 +rect 22 29 24 37 +rect 30 29 32 37 +rect 38 33 40 37 +rect 14 17 16 23 +rect 22 17 24 23 +rect 30 17 32 23 +rect 38 17 40 23 +<< ptransistor >> +rect 22 54 24 57 +rect 30 54 32 57 +<< ndiffusion >> +rect 13 33 14 37 +rect 16 33 17 37 +rect 21 33 22 37 +rect 17 29 22 33 +rect 24 29 25 37 +rect 29 29 30 37 +rect 32 33 33 37 +rect 37 33 38 37 +rect 40 33 41 37 +rect 32 29 37 33 +rect 9 21 14 23 +rect 13 17 14 21 +rect 16 17 22 23 +rect 24 17 25 23 +rect 29 17 30 23 +rect 32 17 38 23 +rect 40 21 45 23 +rect 40 17 41 21 +<< pdiffusion >> +rect 21 54 22 57 +rect 24 54 25 57 +rect 29 54 30 57 +rect 32 54 33 57 +<< ndcontact >> +rect 9 33 13 37 +rect 17 33 21 37 +rect 25 29 29 37 +rect 33 33 37 37 +rect 41 33 45 37 +rect 9 17 13 21 +rect 25 17 29 23 +rect 41 17 45 21 +<< pdcontact >> +rect 17 54 21 58 +rect 25 54 29 58 +rect 33 54 37 58 +<< psubstratepcontact >> +rect 25 9 29 13 +<< nsubstratencontact >> +rect 25 68 29 72 +<< polysilicon >> +rect 22 57 24 60 +rect 30 57 32 60 +rect 22 44 24 54 +rect 30 51 32 54 +rect 31 47 32 51 +rect 14 37 16 44 +rect 22 40 23 44 +rect 22 37 24 40 +rect 30 37 32 47 +rect 38 37 40 44 +rect 14 31 16 33 +rect 38 31 40 33 +rect 14 23 16 24 +rect 22 23 24 29 +rect 30 23 32 29 +rect 38 23 40 24 +rect 14 15 16 17 +rect 22 15 24 17 +rect 30 15 32 17 +rect 38 15 40 17 +<< polycontact >> +rect 27 47 31 51 +rect 10 40 14 44 +rect 23 40 27 44 +rect 40 40 44 44 +rect 12 24 16 28 +rect 38 24 42 28 +<< metal1 >> +rect 0 68 25 72 +rect 29 68 54 72 +rect 0 61 54 65 +rect 10 44 14 61 +rect 29 54 33 58 +rect 17 51 20 54 +rect 17 47 27 51 +rect 17 37 20 47 +rect 34 44 37 54 +rect 27 40 37 44 +rect 40 44 44 61 +rect 34 37 37 40 +rect 6 33 9 37 +rect 45 33 48 37 +rect 25 23 29 29 +rect 25 13 29 17 +rect 0 9 25 13 +rect 29 9 54 13 +rect 0 2 16 6 +rect 20 2 34 6 +rect 38 2 54 6 +<< m2contact >> +rect 25 68 29 72 +rect 25 54 29 58 +rect 2 33 6 37 +rect 48 33 52 37 +rect 16 24 20 28 +rect 34 24 38 28 +rect 9 17 13 21 +rect 41 17 45 21 +rect 16 2 20 6 +rect 34 2 38 6 +<< metal2 >> +rect 2 37 6 72 +rect 2 0 6 33 +rect 9 21 13 72 +rect 25 58 29 68 +rect 9 0 13 17 +rect 16 6 20 24 +rect 34 6 38 24 +rect 41 21 45 72 +rect 41 0 45 17 +rect 48 37 52 72 +rect 48 0 52 33 +<< comment >> +rect 0 0 54 70 +<< labels >> +rlabel metal1 19 63 19 63 1 wl0 +rlabel metal1 19 70 19 70 5 vdd +rlabel metal1 27 4 27 4 1 wl1 +rlabel psubstratepcontact 27 11 27 11 1 gnd +rlabel metal2 4 7 4 7 2 bl0 +rlabel metal2 11 7 11 7 1 bl1 +rlabel metal2 43 7 43 7 1 br1 +rlabel metal2 50 7 50 7 8 br0 +<< end >> diff --git a/technology/scn4m_subm/mag_lib/replica_cell_6t.mag b/technology/scn4m_subm/mag_lib/replica_cell_6t.mag index d0dc472f..c28cb2c6 100644 --- a/technology/scn4m_subm/mag_lib/replica_cell_6t.mag +++ b/technology/scn4m_subm/mag_lib/replica_cell_6t.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1536091380 +timestamp 1541443051 << nwell >> rect -8 29 42 51 << pwell >> @@ -76,15 +76,15 @@ rect 17 6 21 10 rect -2 44 15 48 rect 19 44 32 48 rect -2 40 2 44 +rect 22 40 26 44 rect 32 40 36 44 rect 11 36 12 40 rect 26 36 27 40 rect -2 26 2 29 -rect 11 22 15 36 +rect -2 16 2 22 +rect 11 18 15 36 rect 23 24 27 36 -rect -2 18 15 22 rect 25 20 27 24 -rect -2 16 2 18 rect 14 14 15 18 rect 23 18 27 20 rect 32 26 36 29 diff --git a/technology/scn4m_subm/mag_lib/setup.tcl b/technology/scn4m_subm/mag_lib/setup.tcl new file mode 100644 index 00000000..caf7550b --- /dev/null +++ b/technology/scn4m_subm/mag_lib/setup.tcl @@ -0,0 +1,16 @@ +# Setup file for netgen +ignore class c +equate class {-circuit1 nfet} {-circuit2 n} +equate class {-circuit1 pfet} {-circuit2 p} +# This circuit has symmetries and needs to be flattened to resolve them +# or the banks won't pass +flatten class {-circuit1 bitcell_array} +flatten class {-circuit1 precharge_array_1} +flatten class {-circuit1 precharge_array_2} +flatten class {-circuit1 precharge_array_3} +flatten class {-circuit1 precharge_array_4} +property {-circuit1 nfet} remove as ad ps pd +property {-circuit1 pfet} remove as ad ps pd +property {-circuit2 n} remove as ad ps pd +property {-circuit2 p} remove as ad ps pd +permute transistors diff --git a/technology/scn4m_subm/sp_lib/cell_1rw_1r.sp b/technology/scn4m_subm/sp_lib/cell_1rw_1r.sp new file mode 100644 index 00000000..37f25f75 --- /dev/null +++ b/technology/scn4m_subm/sp_lib/cell_1rw_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT cell_1rw_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1 gnd n w=1.2u l=0.4u +MM8 RA_to_R_right Q gnd gnd n w=1.2u l=0.4u +MM7 RA_to_R_left Q_bar gnd gnd n w=1.2u l=0.4u +MM6 RA_to_R_left wl1 bl1 gnd n w=1.2u l=0.4u +MM5 Q wl0 bl0 gnd n w=0.8u l=0.4u +MM4 Q_bar wl0 br0 gnd n w=0.8u l=0.4u +MM1 Q Q_bar gnd gnd n w=1.6u l=0.4u +MM0 Q_bar Q gnd gnd n w=1.6u l=0.4u +MM3 Q Q_bar vdd vdd p w=0.6u l=0.4u +MM2 Q_bar Q vdd vdd p w=0.6u l=0.4u +.ENDS + diff --git a/technology/scn4m_subm/sp_lib/cell_6t.sp b/technology/scn4m_subm/sp_lib/cell_6t.sp index 846cc371..bb430893 100644 --- a/technology/scn4m_subm/sp_lib/cell_6t.sp +++ b/technology/scn4m_subm/sp_lib/cell_6t.sp @@ -3,11 +3,16 @@ .SUBCKT cell_6t bl br wl vdd gnd * SPICE3 file created from cell_6t.ext - technology: scmos -M1000 a_36_40# a_28_32# vdd vdd p w=0.6u l=0.8u -M1001 vdd a_36_40# a_28_32# vdd p w=0.6u l=0.8u -M1002 a_36_40# a_28_32# gnd gnd n w=1.6u l=0.4u -M1003 gnd a_36_40# a_28_32# gnd n w=1.6u l=0.4u -M1004 a_36_40# wl bl gnd n w=0.8u l=0.4u -M1005 a_28_32# wl br gnd n w=0.8u l=0.4u +* Inverter 1 +M1000 Q Qbar vdd vdd p w=0.6u l=0.8u +M1002 Q Qbar gnd gnd n w=1.6u l=0.4u + +* Inverter 2 +M1001 vdd Q Qbar vdd p w=0.6u l=0.8u +M1003 gnd Q Qbar gnd n w=1.6u l=0.4u + +* Access transistors +M1004 Q wl bl gnd n w=0.8u l=0.4u +M1005 Qbar wl br gnd n w=0.8u l=0.4u .ENDS diff --git a/technology/scn4m_subm/sp_lib/replica_cell_1rw_1r.sp b/technology/scn4m_subm/sp_lib/replica_cell_1rw_1r.sp new file mode 100644 index 00000000..94bdb75e --- /dev/null +++ b/technology/scn4m_subm/sp_lib/replica_cell_1rw_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT replica_cell_1rw_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1 gnd n w=1.2u l=0.4u +MM8 RA_to_R_right Q gnd gnd n w=1.2u l=0.4u +MM7 RA_to_R_left vdd gnd gnd n w=1.2u l=0.4u +MM6 RA_to_R_left wl1 bl1 gnd n w=1.2u l=0.4u +MM5 Q wl0 bl0 gnd n w=0.8u l=0.4u +MM4 vdd wl0 br0 gnd n w=0.8u l=0.4u +MM1 Q vdd gnd gnd n w=1.6u l=0.4u +MM0 vdd Q gnd gnd n w=1.6u l=0.4u +MM3 Q vdd vdd vdd p w=0.6u l=0.4u +MM2 vdd Q vdd vdd p w=0.6u l=0.4u +.ENDS + diff --git a/technology/scn4m_subm/sp_lib/replica_cell_6t.sp b/technology/scn4m_subm/sp_lib/replica_cell_6t.sp index d26d600f..a9d41398 100644 --- a/technology/scn4m_subm/sp_lib/replica_cell_6t.sp +++ b/technology/scn4m_subm/sp_lib/replica_cell_6t.sp @@ -1,14 +1,18 @@ *********************** "cell_6t" ****************************** .SUBCKT replica_cell_6t bl br wl vdd gnd -* SPICE3 file created from replica_cell_6t.ext - technology: scmos +* SPICE3 file created from cell_6t.ext - technology: scmos -M1000 gnd a_28_32# vdd vdd p w=0.6u l=0.8u -M1001 vdd gnd a_28_32# vdd p w=0.6u l=0.8u -** SOURCE/DRAIN TIED -M1002 gnd a_28_32# gnd gnd n w=1.6u l=0.4u -M1003 gnd gnd a_28_32# gnd n w=1.6u l=0.4u -M1004 gnd wl bl gnd n w=0.8u l=0.4u -M1005 a_28_32# wl br gnd n w=0.8u l=0.4u +* Inverter 1 +M1000 Q vdd vdd vdd p w=0.6u l=0.8u +M1002 Q vdd gnd gnd n w=1.6u l=0.4u + +* Inverter 2 +M1001 vdd Q vdd vdd p w=0.6u l=0.8u +M1003 gnd Q vdd gnd n w=1.6u l=0.4u + +* Access transistors +M1004 Q wl bl gnd n w=0.8u l=0.4u +M1005 vdd wl br gnd n w=0.8u l=0.4u .ENDS diff --git a/technology/scn4m_subm/sp_lib/write_driver.sp b/technology/scn4m_subm/sp_lib/write_driver.sp index afcf1049..d1dbf9b2 100644 --- a/technology/scn4m_subm/sp_lib/write_driver.sp +++ b/technology/scn4m_subm/sp_lib/write_driver.sp @@ -1,23 +1,37 @@ *********************** Write_Driver ****************************** .SUBCKT write_driver din bl br en vdd gnd -* SPICE3 file created from write_driver.ext - technology: scmos -M1000 a_44_708# a_36_700# bl gnd n w=2.4u l=0.4u -M1001 br a_16_500# a_44_708# gnd n w=2.4u l=0.4u -M1002 a_44_708# en gnd gnd n w=2.4u l=0.4u -M1003 gnd a_8_284# a_16_500# gnd n w=0.8u l=0.4u -M1004 a_36_700# a_20_328# gnd gnd n w=0.8u l=0.4u -M1005 vdd a_8_284# a_16_500# vdd p w=1.4u l=0.4u -M1006 a_36_700# a_20_328# vdd vdd p w=1.4u l=0.4u -M1007 vdd en a_20_328# vdd p w=1.4u l=0.4u -M1008 a_20_328# a_64_360# vdd vdd p w=1.4u l=0.4u -M1009 a_48_328# en a_20_328# gnd n w=1.4u l=0.4u -M1010 gnd a_64_360# a_48_328# gnd n w=1.4u l=0.4u -M1011 a_40_228# en a_8_284# gnd n w=1.4u l=0.4u -M1012 gnd din a_40_228# gnd n w=1.4u l=0.4u -M1013 a_64_360# din gnd gnd n w=0.8u l=0.4u -M1014 a_8_284# en vdd vdd p w=1.4u l=0.4u -M1015 vdd din a_8_284# vdd p w=1.4u l=0.4u -M1016 a_64_360# din vdd vdd p w=1.4u l=0.4u +**** Inverter to conver Data_in to data_in_bar ****** +* din_bar = inv(din) +M_1 din_bar din gnd gnd n W=0.8u L=0.4u +M_2 din_bar din vdd vdd p W=1.4u L=0.4u -.ENDS +**** 2input nand gate follwed by inverter to drive BL ****** +* din_bar_gated = nand(en, din) +M_3 din_bar_gated en net_7 gnd n W=1.4u L=0.4u +M_4 net_7 din gnd gnd n W=1.4u L=0.4u +M_5 din_bar_gated en vdd vdd p W=1.4u L=0.4u +M_6 din_bar_gated din vdd vdd p W=1.4u L=0.4u +* din_bar_gated_bar = inv(din_bar_gated) +M_7 din_bar_gated_bar din_bar_gated vdd vdd p W=1.4u L=0.4u +M_8 din_bar_gated_bar din_bar_gated gnd gnd n W=0.8u L=0.4u + +**** 2input nand gate follwed by inverter to drive BR****** +* din_gated = nand(en, din_bar) +M_9 din_gated en vdd vdd p W=1.4u L=0.4u +M_10 din_gated en net_8 gnd n W=1.4u L=0.4u +M_11 net_8 din_bar gnd gnd n W=1.4u L=0.4u +M_12 din_gated din_bar vdd vdd p W=1.4u L=0.4u +* din_gated_bar = inv(din_gated) +M_13 din_gated_bar din_gated vdd vdd p W=1.4u L=0.4u +M_14 din_gated_bar din_gated gnd gnd n W=0.8u L=0.4u + +************************************************ +* pull down with en enable +M_15 bl din_gated_bar net_5 gnd n W=2.4u L=0.4u +M_16 br din_bar_gated_bar net_5 gnd n W=2.4u L=0.4u +M_17 net_5 en gnd gnd n W=2.4u L=0.4u + + + +.ENDS $ write_driver diff --git a/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech b/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech index bb2c2490..f39aa84f 100644 --- a/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech +++ b/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech @@ -1,5 +1,5 @@ tech - format 29 + format 31 scmos end @@ -301,11 +301,6 @@ style lambda=0.20(p) scalefactor 20 10 options calma-permissive-labels - # This is a custom section to add bounding boxes in OpenRAM - layer BB bb - labels bb - calma 63 0 - layer CWN nwell,rnw,nwr,nwsd,nwsc bloat-or pdiff,apres,rpd,pdc/a,pfet * 120 bloat-or nsd,nsc/a * 60 @@ -1769,11 +1764,6 @@ cifinput style lambda=0.20(p) scalefactor 20 - # This is a custom section to add bounding boxes in OpenRAM - layer bb BB - labels BB - calma 63 0 - layer nwell CWN and-not CWNR and-not CTA @@ -6701,7 +6691,7 @@ drc edge4way nfet,pfet,fet space/active,ndiff,anres,rnd,ndc/a,pdiff,apres,rpd,pdc/a 3 ndiff,anres,rnd,ndc/a,pdiff,apres,rpd,pdc/a,nfet,pfet,fet 0 0 \ "N-Diffusion,P-Diffusion overhang of Transistor < 3 (Mosis #3.4)" active - edge4way poly,fp,rp,pc/a ~(poly,fp,pres,rp,pc/a,nfet,pfet,fet,prp)/active 1 space space 1 \ + edge4way poly,fp,rp,pc/a ~(poly,fp,pres,rp,pc/a,nfet,pfet,fet,prp)/active 1 space/a space/a 1 \ "Poly spacing to Diffusion < 1 (Mosis #3.5)" edge4way nfet ~(nfet)/active 2 ~(pselect)/select ~(nfet)/active 2 \ @@ -7212,13 +7202,15 @@ extract planeorder via3 14 planeorder fill 15 + substrate *psd,space/w,pwell well + resist (ndiff,anres,rnd,ndc,nsd,nwsd,nsc,nwsc)/active 3700 resist (pdiff,apres,rpd,pdc,psd,psc)/active 2800 resist (nwell)/well 1018000 - resist (rnw,nwr)/active 1018000 + resist (rnw,nwr)/active 1018000 0.5 resist (pwell)/well 1 resist (poly,fp,rp,pc,pc,nfet,pfet,fet)/active 6000 - resist (pres)/active 6000 + resist (pres)/active 6000 0.5 resist (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c,m2c)/metal1 80 resist (m2,fm2,rm2,m2c,m3c,m3c)/metal2 70 resist (m3,fm3,rm3,m3c,m4c,m4c)/metal3 80 @@ -7416,33 +7408,30 @@ extract #metali -#fets +#devices - fet pfet pdiff,pdc 2 pfet Vdd! nwell 52 181 - fet pfet pdiff,pdc 1 pfet Vdd! nwell 52 181 - - fet nfet ndiff,ndc 2 nfet Gnd! pwell 55 182 - fet nfet ndiff,ndc 1 nfet Gnd! pwell 55 182 + device mosfet pfet pfet pdiff,pdc nwell ERROR 52 181 + device mosfet nfet nfet ndiff,ndc pwell,space/w ERROR 55 182 fetresis pfet linear 12182 fetresis pfet saturation 12182 fetresis nfet linear 3961 fetresis nfet saturation 3961 - fet rnwell nsd,nsc 2 nwellResistor Gnd! nwell,pwell 0 0 - fet rpoly poly,pc 2 polyResistor Gnd! nwell,pwell 0 0 - fet nwr nwsd 2 nwellFig1bResistor Gnd! nwell,pwell 0 0 - fet rndiff ndiff,ndc 2 ndiffResistor Gnd! nwell,pwell 0 0 - fet rpdiff pdiff,pdc 2 pdiffResistor Gnd! nwell,pwell 0 0 + device resistor nwellResistor rnwell *nsd + device resistor polyResistor rpoly *poly + device resistor nwellFig1bResistor nwr nwsd + device resistor ndiffResistor rndiff *ndiff + device resistor pdiffResistor rpdiff *pdiff - fet rmetal1 metal1 2 metal1Resistor Gnd! nwell,pwell 0 0 - fet rmetal2 metal2 2 metal2Resistor Gnd! nwell,pwell 0 0 - fet rmetal3 metal3 2 metal3Resistor Gnd! nwell,pwell 0 0 - fet rmetal4 metal4 2 metal4Resistor Gnd! nwell,pwell 0 0 + device resistor metal1Resistor rmetal1 *metal1 + device resistor metal2Resistor rmetal2 *metal2 + device resistor metal3Resistor rmetal3 *metal3 + device resistor metal4Resistor rmetal4 *metal4 - fet pres poly,pc 2 presResistor Gnd! nwell,pwell 0 0 - fet anres ndiff,ndc 2 anresResistor Gnd! nwell,pwell 0 0 - fet apres pdiff,pdc 2 apresResistor Gnd! nwell,pwell 0 0 + device resistor presResistor pres *poly + device resistor anresResistor anres *ndiff + device resistor apresResistor apres *pdiff end diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index fc7440e1..3b7e2142 100755 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -1,15 +1,10 @@ import os +from design_rules import * """ File containing the process technology parameters for SCMOS 3me, subm, 180nm. """ -info={} -info["name"]="scn3me_subm" -info["body_tie_down"] = 0 -info["has_pwell"] = True -info["has_nwell"] = True - #GDS file info GDS={} # gds units @@ -59,16 +54,25 @@ parameter={} parameter["min_tx_size"] = 4*_lambda_ parameter["beta"] = 2 +parameter["6T_inv_nmos_size"] = 8*_lambda_ +parameter["6T_inv_pmos_size"] = 3*_lambda_ +parameter["6T_access_size"] = 4*_lambda_ + drclvs_home=os.environ.get("DRCLVS_HOME") -drc={} +drc = design_rules("scn4me_sub") + +drc["body_tie_down"] = 0 +drc["has_pwell"] = True +drc["has_nwell"] = True + #grid size is 1/2 a lambda drc["grid"]=0.5*_lambda_ + #DRC/LVS test set_up drc["drc_rules"]=drclvs_home+"/calibreDRC_scn3me_subm.rul" drc["lvs_rules"]=drclvs_home+"/calibreLVS_scn3me_subm.rul" drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/scn3me_subm/layers.map" - # minwidth_tx with contact (no dog bone transistors) drc["minwidth_tx"] = 4*_lambda_ @@ -236,7 +240,7 @@ spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+" #spice stimulus related variables -spice["feasible_period"] = 5 # estimated feasible period in ns +spice["feasible_period"] = 10 # estimated feasible period in ns spice["supply_voltages"] = [4.5, 5.0, 5.5] # Supply voltage corners in [Volts] spice["nom_supply_voltage"] = 5.0 # Nominal supply voltage in [Volts] spice["rise_time"] = 0.05 # rise time in [Nano-seconds] @@ -257,6 +261,7 @@ spice["clk"] = "clk" # analytical delay parameters # FIXME: These need to be updated for SCMOS, they are copied from FreePDK45. +spice["v_threshold_typical"] = 1.3 # Typical Threshold voltage in Volts spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2 spice["min_tx_r"] = 9250.0 # Minimum transistor on resistance in ohms @@ -288,6 +293,14 @@ spice["inv_transition_prob"] = .5 # Transition probability of inverter. spice["nand2_transition_prob"] = .1875 # Transition probability of 2-input nand. spice["nand3_transition_prob"] = .1094 # Transition probability of 3-input nand. spice["nor2_transition_prob"] = .1875 # Transition probability of 2-input nor. + +#Logical Effort relative values for the Handmade cells +parameter["dff_clk_cin"] = 27.5 +parameter["6tcell_wl_cin"] = 2 +parameter["min_inv_para_delay"] = .5 +parameter["sa_en_pmos_size"] = 24*_lambda_ +parameter["sa_en_nmos_size"] = 9*_lambda_ + ################################################### ##END Spice Simulation Parameters ################################################### diff --git a/technology/scn4m_subm/tf/display.drf b/technology/scn4m_subm/tf/display.drf index aeeefe2c..39349998 100644 --- a/technology/scn4m_subm/tf/display.drf +++ b/technology/scn4m_subm/tf/display.drf @@ -546,7 +546,7 @@ drDefinePacket( ( display background stipple1 lineStyle0 black black outlineStipple) ( display y9 stipple0 solid silver silver solid ) ( display Metal3Net dots4 solid navy navy outlineStipple) - ( display Metal3Net dots4 solid tan tan outlineStipple) + ( display Metal4Net dots4 solid tan tan outlineStipple) ( display A1 stipple0 lineStyle0 winBack winBack solid ) ( display pin solid lineStyle0 red red solid ) ( display XPNet blank solid yellow yellow outline ) @@ -640,6 +640,7 @@ drDefinePacket( ( display Canplace blank solid cyan cyan outline ) ( display annotate7 stipple0 solid red red solid ) ( display Via2 solid solid navy navy solid ) + ( display Via3 solid solid tan tan solid ) ( display Metal2Pin stipple0 lineStyle0 magenta magenta solid ) ( display annotate4 stipple0 solid yellow yellow solid ) ( display device1 stipple1 lineStyle0 green green outlineStipple) diff --git a/technology/scn4m_subm/tf/glade_scn4me_subm.py b/technology/scn4m_subm/tf/glade_scn4m_subm.py similarity index 64% rename from technology/scn4m_subm/tf/glade_scn4me_subm.py rename to technology/scn4m_subm/tf/glade_scn4m_subm.py index d2f9aa7e..314727c3 100644 --- a/technology/scn4m_subm/tf/glade_scn4me_subm.py +++ b/technology/scn4m_subm/tf/glade_scn4m_subm.py @@ -1,5 +1,5 @@ import os -CWD = os.environ.get("OPENRAM_TECH") + "/scn3me_subm/tf" +CWD = os.environ.get("OPENRAM_TECH") + "/scn4m_subm/tf" ui().importCds("default", CWD+"/display.drf", CWD+"/mosis.tf", 1000, 1, CWD+"/layers.map") diff --git a/technology/scn4m_subm/tf/mosis.tf b/technology/scn4m_subm/tf/mosis.tf index e48d76a0..bae7f07a 100644 --- a/technology/scn4m_subm/tf/mosis.tf +++ b/technology/scn4m_subm/tf/mosis.tf @@ -147,6 +147,8 @@ layerDefinitions( ( Metal2 drawing ) ( Via2 drawing ) ( Metal3 drawing ) + ( Via3 drawing ) + ( Metal4 drawing ) ( annotate drawing ) ( annotate drawing1 ) ( annotate drawing2 ) @@ -161,6 +163,7 @@ layerDefinitions( ( Metal1 pin ) ( Metal2 pin ) ( Metal3 pin ) + ( Metal4 pin ) ( Glass drawing ) ( XP drawing ) ( prBoundary drawing ) @@ -203,6 +206,8 @@ layerDefinitions( ( Via net ) ( Metal3 net ) ( Via2 net ) + ( Metal4 net ) + ( Via3 net ) ( pin label ) ( text drawing ) ( pin drawing ) @@ -313,11 +318,14 @@ layerDefinitions( ( annotate drawing9 annotate9 t t nil t nil ) ( Via2 drawing Via2 t t t t t ) ( Metal3 drawing Metal3 t t t t t ) + ( Via3 drawing Via3 t t t t t ) + ( Metal4 drawing Metal4 t t t t t ) ( Glass drawing Glass t t t nil t ) ( XP drawing XP t t t nil t ) ( Metal1 pin Metal1Pin t t t nil t ) ( Metal2 pin Metal2Pin t t t nil t ) ( Metal3 pin Metal3Pin t t t nil t ) + ( Metal4 pin Metal4Pin t t t nil t ) ( Poly1 pin Poly1Pin t t t nil t ) ( prBoundary drawing prBoundary t t nil t nil ) ( prBoundary boundary prBoundaryBnd t t nil t nil ) @@ -356,6 +364,7 @@ layerDefinitions( ( device annotate deviceAnt t t t t nil ) ( Metal2 net Metal2Net t t t nil nil ) ( Metal3 net Metal3Net t t t nil nil ) + ( Metal4 net Metal4Net t t t nil nil ) ( device label deviceLbl t t t t nil ) ( Via net ViaNet t t t nil nil ) ( Via2 net Via2Net t t t nil nil )