├── .gitignore ├── docs └── images │ ├── fakeram.png │ ├── openram.png │ ├── fakeram_io.png │ ├── fakeram_power.png │ └── openram_pins.png ├── patches ├── nmlimitremoval_patch.sh └── cacti.patch ├── Makefile ├── example_cfgs ├── freepdk45.cfg └── sky130.cfg ├── LICENSE ├── scripts ├── utils │ ├── class_process.py │ ├── cacti_config.py │ ├── class_memory.py │ ├── generate_verilog.py │ ├── generate_lef.py │ └── generate_lib.py └── run.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | tools/ 2 | results/ 3 | __pycache__/ 4 | -------------------------------------------------------------------------------- /docs/images/fakeram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bespoke-silicon-group/bsg_fakeram/HEAD/docs/images/fakeram.png -------------------------------------------------------------------------------- /docs/images/openram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bespoke-silicon-group/bsg_fakeram/HEAD/docs/images/openram.png -------------------------------------------------------------------------------- /docs/images/fakeram_io.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bespoke-silicon-group/bsg_fakeram/HEAD/docs/images/fakeram_io.png -------------------------------------------------------------------------------- /docs/images/fakeram_power.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bespoke-silicon-group/bsg_fakeram/HEAD/docs/images/fakeram_power.png -------------------------------------------------------------------------------- /docs/images/openram_pins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bespoke-silicon-group/bsg_fakeram/HEAD/docs/images/openram_pins.png -------------------------------------------------------------------------------- /patches/nmlimitremoval_patch.sh: -------------------------------------------------------------------------------- 1 | if ! grep -Fxq '// else if (F_sz_um > 0.091)' ./tools/cacti/io.cc 2 | then 3 | sed -i '/else if (F_sz_um > 0.091)/,/\}/'' s/^/\/\//' ./tools/cacti/io.cc 4 | fi 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export TOP_DIR :=$(shell git rev-parse --show-toplevel) 2 | 3 | export CACTI_BUILD_DIR := $(TOP_DIR)/tools/cacti 4 | 5 | CONFIG := $(TOP_DIR)/example_cfgs/freepdk45.cfg 6 | 7 | OUT_DIR := $(TOP_DIR)/results 8 | 9 | run: 10 | ./scripts/run.py $(CONFIG) --output_dir $(OUT_DIR) 11 | 12 | view.%: 13 | klayout ./$(OUT_DIR)/$*/$*.lef & 14 | 15 | clean: 16 | rm -rf ./$(OUT_DIR) 17 | 18 | #======================================= 19 | # TOOLS 20 | #======================================= 21 | 22 | tools: $(CACTI_BUILD_DIR) 23 | 24 | $(CACTI_BUILD_DIR): 25 | mkdir -p $(@D) 26 | git clone https://github.com/HewlettPackard/Cacti.git $@ 27 | cd $@; git checkout 1ffd8dfb10303d306ecd8d215320aea07651e878 28 | cd $@; git apply $(TOP_DIR)/patches/cacti.patch 29 | sh $(TOP_DIR)/patches/nmlimitremoval_patch.sh 30 | cd $@; make -j4 31 | 32 | clean_tools: 33 | rm -rf $(CACTI_BUILD_DIR) 34 | 35 | -------------------------------------------------------------------------------- /example_cfgs/freepdk45.cfg: -------------------------------------------------------------------------------- 1 | { 2 | # The process node. This is used to tell cacti what technology to use when 3 | # estimating power, performance and area numbers. 4 | "tech_nm": 45, 5 | 6 | # The operating voltage. 7 | "voltage": 1.1, 8 | 9 | # String to add in front of every metal layer number for the layer name. 10 | "metalPrefix": "metal", 11 | 12 | # The pin width for signal pins. 13 | "pinWidth_nm": 70, 14 | 15 | # The minimum pin pitch for signal pins (all pins will have a pitch that is a 16 | # multuple of this pitch. The first pin will be a multiple of this pitch from 17 | # the bottom edge of the macro too. 18 | "pinPitch_nm": 140, 19 | 20 | # Optional: snap the width and height of the sram to a multiple value. 21 | "snapWidth_nm": 190, 22 | "snapHeight_nm": 1400, 23 | 24 | # Flips the pin orientations. Non-fliped assumes metal1 is vertical therefore 25 | # supply pins on metal4 will be horizontal and signal pins will also be on 26 | # metal4. If set to true, supply pins on metal4 will be vertical and signal 27 | # pins will be on metal3. 28 | "flipPins": true, 29 | 30 | # List of SRAM configurations (name, width, depth, and banks) 31 | "srams": [ 32 | {"name": "sram_32x32_1rw", "width": 32, "depth": 32, "banks": 1}, 33 | {"name": "sram_8x512_1rw", "width": 8, "depth": 512, "banks": 1} 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /example_cfgs/sky130.cfg: -------------------------------------------------------------------------------- 1 | { 2 | # The process node. This is used to tell cacti what technology to use when 3 | # estimating power, performance and area numbers. 4 | "tech_nm": 130, 5 | 6 | # The operating voltage. 7 | "voltage": 1.8, 8 | 9 | # String to add in front of every metal layer number for the layer name. 10 | "metalPrefix": "met", 11 | 12 | # The pin width for signal pins. 13 | "pinWidth_nm": 300, 14 | 15 | # The pin length for the signal pins. 16 | "pinHeight_nm": 800, 17 | 18 | # The minimum pin pitch for signal pins (all pins will have a pitch that is a 19 | # multuple of this pitch. The first pin will be a multiple of this pitch from 20 | # the bottom edge of the macro too. 21 | "pinPitch_nm": 600, 22 | 23 | # Optional: snap the width and height of the sram to a multiple value. 24 | "snapWidth_nm": 460, 25 | "snapHeight_nm": 2720, 26 | 27 | # Flips the pin orientations. Non-fliped assumes metal1 is vertical therefore 28 | # supply pins on metal4 will be horizontal and signal pins will also be on 29 | # metal4. If set to true, supply pins on metal4 will be vertical and signal 30 | # pins will be on metal3. 31 | "flipPins": true, 32 | 33 | # List of SRAM configurations (name, width, depth, and banks) 34 | "srams": [ 35 | {"name": "fakeram130_1024x32", "width": 32, "depth": 1024, "banks": 1}, 36 | {"name": "fakeram130_64x32", "width": 32, "depth": 64, "banks": 1} 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, Bespoke Silicon Group 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /scripts/utils/class_process.py: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # PROCESS CLASS 3 | # 4 | # This class stores the infromation about the process that the memory is being 5 | # generated in. Every memory has a pointer to a process object. The information 6 | # for the process comes from the json configuration file (typically before the 7 | # "sram" list section). 8 | ################################################################################ 9 | 10 | class Process: 11 | 12 | def __init__(self, json_data): 13 | 14 | # From JSON configuration file 15 | self.tech_nm = int(json_data['tech_nm']) 16 | self.metalPrefix = str(json_data['metalPrefix']) 17 | self.pinWidth_nm = int(json_data['pinWidth_nm']) 18 | self.pinPitch_nm = int(json_data['pinPitch_nm']) 19 | self.voltage = str(json_data['voltage']) 20 | 21 | # Optional keys 22 | self.snapWidth_nm = int(json_data['snapWidth_nm']) if 'snapWidth_nm' in json_data else 1 23 | self.snapHeight_nm = int(json_data['snapHeight_nm']) if 'snapHeight_nm' in json_data else 1 24 | self.flipPins = str(json_data['flipPins']) if 'flipPins' in json_data else 'false' 25 | self.pinHeight_nm = int(json_data['pinHeight_nm']) if 'pinHeight_nm' in json_data else (self.pinWidth_nm) # Default to square pins 26 | self.vlogTimingCheckSignalExpansion = bool(json_data['vlogTimingCheckSignalExpansion']) if 'vlogTimingCheckSignalExpansion' in json_data else False 27 | 28 | # Converted values 29 | self.tech_um = self.tech_nm / 1000.0 30 | self.pinWidth_um = self.pinWidth_nm / 1000.0 31 | self.pinHeight_um = self.pinHeight_nm / 1000.0 32 | self.pinPitch_um = self.pinPitch_nm / 1000.0 33 | 34 | -------------------------------------------------------------------------------- /scripts/run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import json 5 | import argparse 6 | 7 | from utils.class_process import Process 8 | from utils.class_memory import Memory 9 | 10 | from utils.generate_lib import generate_lib 11 | from utils.generate_lef import generate_lef 12 | from utils.generate_verilog import generate_verilog 13 | from utils.generate_verilog import generate_verilog_bb 14 | 15 | ################################################################################ 16 | # RUN GENERATOR 17 | # 18 | # This is the main part of the script. It will read in the JSON configuration 19 | # file, create a Cacti configuration file, run Cacti, extract the data from 20 | # Cacti, and then generate the timing, physical and logical views for each SRAM 21 | # found in the JSON configuration file. 22 | ################################################################################ 23 | 24 | def get_args() -> argparse.Namespace: 25 | """ 26 | Get command line arguments 27 | """ 28 | parser = argparse.ArgumentParser( 29 | description=""" 30 | BSG Black-box SRAM Generator -- 31 | This project is designed to generate black-boxed SRAMs for use in CAD 32 | flows where either an SRAM generator is not avaible or doesn't 33 | exist. """ 34 | ) 35 | 36 | parser.add_argument("config", help="JSON configuration file") 37 | 38 | parser.add_argument( 39 | "--output_dir", action="store", help="Output directory ", required=False, default=None 40 | ) 41 | 42 | parser.add_argument( 43 | "--cacti_dir", action="store", help="CACTI installation directory ", required=False, default=None 44 | ) 45 | 46 | return parser.parse_args() 47 | 48 | 49 | def main ( args : argparse.Namespace): 50 | 51 | # Load the JSON configuration file 52 | with open(args.config, 'r') as fid: 53 | raw = [line.strip() for line in fid if not line.strip().startswith('#')] 54 | json_data = json.loads('\n'.join(raw)) 55 | 56 | # Create a process object (shared by all srams) 57 | process = Process(json_data) 58 | 59 | # Go through each sram and generate the lib, lef and v files 60 | for sram_data in json_data['srams']: 61 | memory = Memory(process, sram_data, args.output_dir, args.cacti_dir) 62 | generate_lib(memory) 63 | generate_lef(memory) 64 | generate_verilog(memory, tmChkExpand=process.vlogTimingCheckSignalExpansion) 65 | generate_verilog_bb(memory) 66 | 67 | ### Entry point 68 | if __name__ == '__main__': 69 | args = get_args() 70 | main( args ) 71 | 72 | -------------------------------------------------------------------------------- /scripts/utils/cacti_config.py: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # CACTI CONFIG 3 | # 4 | # This list is used to write out the Cacti configuration file for the SRAMs we 5 | # are generating so we can extract the power, timing and area numbers. 6 | ################################################################################ 7 | 8 | # This configuration is baed on the cacti/cache.cfg file which is considered 9 | # the default according to the cacti technical report. 10 | 11 | # **WARNING** the very first line in this file cannot be blank otherwise cacti 12 | # stalls forever... 13 | 14 | cacti_config = '''# cacti.cfg 15 | -size (bytes) {0} 16 | -block size (bytes) {1} 17 | -read-write port {2} 18 | -exclusive read port {3} 19 | -exclusive write port {4} 20 | -technology (u) {5} 21 | -output/input bus width {6} 22 | -UCA bank count 1 23 | -Array Power Gating - "false" 24 | -WL Power Gating - "false" 25 | -CL Power Gating - "false" 26 | -Bitline floating - "false" 27 | -Interconnect Power Gating - "false" 28 | -Power Gating Performance Loss 0.01 29 | -associativity 1 30 | -single ended read ports 0 31 | -page size (bits) 8192 32 | -burst length 8 33 | -internal prefetch width 8 34 | -Data array cell type - "itrs-lop" 35 | -Data array peripheral type - "itrs-hp" 36 | -Tag array cell type - "itrs-lop" 37 | -Tag array peripheral type - "itrs-hp" 38 | -operating temperature (K) 300 39 | -cache type "{8}" 40 | -tag size (b) "default" 41 | -access mode (normal, sequential, fast) - "normal" 42 | -design objective (weight delay, dynamic power, leakage power, cycle time, area) 0:0:0:100:0 43 | -deviate (delay, dynamic power, leakage power, cycle time, area) 20:100000:100000:100000:100000 44 | -NUCAdesign objective (weight delay, dynamic power, leakage power, cycle time, area) 100:100:0:0:100 45 | -NUCAdeviate (delay, dynamic power, leakage power, cycle time, area) 10:10000:10000:10000:10000 46 | -Optimize ED or ED^2 (ED, ED^2, NONE): "NONE" 47 | -Cache model (NUCA, UCA) - "UCA" 48 | -NUCA bank count 0 49 | -Wire signaling (fullswing, lowswing, default) - "default" 50 | -Wire inside mat - "default" 51 | -Wire outside mat - "default" 52 | -Interconnect projection - "conservative" 53 | -Core count 8 54 | -Cache level (L2/L3) - "L3" 55 | -Add ECC - "true" 56 | -Print level (DETAILED, CONCISE) - "DETAILED" 57 | -Print input parameters - "true" 58 | -Force cache config - "false" 59 | -Ndwl 1 60 | -Ndbl 1 61 | -Nspd 0 62 | -Ndcm 1 63 | -Ndsam1 0 64 | -Ndsam2 0 65 | -dram_type "DDR3" 66 | -io state "WRITE" 67 | -addr_timing 1.0 68 | -mem_density 4 Gb 69 | -bus_freq 800 MHz 70 | -duty_cycle 1.0 71 | -activity_dq 1.0 72 | -activity_ca 0.5 73 | -num_dq 72 74 | -num_dqs 18 75 | -num_ca 25 76 | -num_clk 2 77 | -num_mem_dq 2 78 | -mem_data_width 4 79 | -rtt_value 10000 80 | -ron_value 34 81 | -tflight_value 82 | -num_bobs 1 83 | -capacity 80 84 | -num_channels_per_bob 1 85 | -first metric "Cost" 86 | -second metric "Bandwidth" 87 | -third metric "Energy" 88 | -DIMM model "ALL" 89 | -mirror_in_bob "F" 90 | ''' 91 | 92 | -------------------------------------------------------------------------------- /scripts/utils/class_memory.py: -------------------------------------------------------------------------------- 1 | import math 2 | import os 3 | import sys 4 | from pathlib import Path 5 | from utils.cacti_config import cacti_config 6 | 7 | ################################################################################ 8 | # MEMORY CLASS 9 | # 10 | # This class stores the infromation about a specific memory that is being 11 | # generated. This class takes in a process object, the infromation in one of 12 | # the items in the "sram" list section of the json configuration file, and 13 | # finally runs cacti to generate the rest of the data. 14 | ################################################################################ 15 | 16 | class Memory: 17 | 18 | def __init__( self, process, sram_data , output_dir = None, cacti_dir = None): 19 | 20 | self.process = process 21 | self.name = str(sram_data['name']) 22 | self.width_in_bits = int(sram_data['width']) 23 | self.depth = int(sram_data['depth']) 24 | self.num_banks = int(sram_data['banks']) 25 | self.cache_type = str(sram_data['type']) if 'type' in sram_data else 'cache' 26 | self.rw_ports = 1 27 | self.width_in_bytes = math.ceil(self.width_in_bits / 8.0) 28 | self.total_size = self.width_in_bytes * self.depth 29 | if output_dir: # Output dir was set by command line option 30 | p = str(Path(output_dir).expanduser().resolve(strict=False)) 31 | self.results_dir = os.sep.join([p, self.name]) 32 | else: 33 | self.results_dir = os.sep.join([os.getcwd(), 'results', self.name]) 34 | if not os.path.exists( self.results_dir ): 35 | os.makedirs( self.results_dir ) 36 | if cacti_dir: 37 | self.cacti_dir = cacti_dir 38 | else: 39 | self.cacti_dir = os.environ['CACTI_BUILD_DIR'] 40 | self.__run_cacti() 41 | with open( os.sep.join([self.results_dir, 'cacti.cfg.out']), 'r' ) as fid: 42 | lines = [line for line in fid] 43 | cacti_data = lines[-1].split(',') 44 | 45 | self.tech_node_nm = int(cacti_data[0]) 46 | self.capacity_bytes = int(cacti_data[1]) 47 | self.associativity = int(cacti_data[2]) 48 | self.output_width_bits = int(cacti_data[3]) 49 | self.access_time_ns = float(cacti_data[4]) 50 | self.cycle_time_ns = float(cacti_data[5]) 51 | #self.dyn_search_energy_nj = float(cacti_data[6]) 52 | self.dyn_read_energy_nj = float(cacti_data[7]) 53 | self.dyn_write_energy_nj = float(cacti_data[8]) 54 | self.standby_leakage_per_bank_mW = float(cacti_data[9]) 55 | self.area_mm2 = float(cacti_data[10]) 56 | self.fo4_ps = float(cacti_data[11]) 57 | self.width_um = float(cacti_data[12]) 58 | self.height_um = float(cacti_data[13]) 59 | 60 | self.cap_input_pf = 0.005 61 | 62 | self.tech_node_um = self.tech_node_nm / 1000.0 63 | 64 | print(f'Original {self.name} size = {self.width_um} x {self.height_um}') 65 | # Adjust to snap 66 | self.width_um = (math.ceil((self.width_um*1000.0)/self.process.snapWidth_nm)*self.process.snapWidth_nm)/1000.0 67 | self.height_um = (math.ceil((self.height_um*1000.0)/self.process.snapHeight_nm)*self.process.snapHeight_nm)/1000.0 68 | self.area_um2 = self.width_um * self.height_um 69 | 70 | #self.pin_dynamic_power_mW = (0.5 * self.cap_input_pf * (float(self.process.voltage)**2))*1e9 ;# P = 0.5*CV^2 71 | self.pin_dynamic_power_mW = self.dyn_write_energy_nj 72 | 73 | self.t_setup_ns = 0.050 ;# arbitrary 50ps setup 74 | self.t_hold_ns = 0.050 ;# arbitrary 50ps hold 75 | 76 | # __run_cacti: shell out to cacti to generate a csv file with more data 77 | # regarding this memory based on the input parameters from the json 78 | # configuration file. 79 | def __run_cacti( self ): 80 | fid = open(os.sep.join([self.results_dir,'cacti.cfg']), 'w') 81 | fid.write( cacti_config.format( self.total_size 82 | , self.width_in_bytes, self.rw_ports, 0, 0 83 | , self.process.tech_um, self.width_in_bytes*8, self.num_banks 84 | , self.cache_type )) 85 | fid.close() 86 | odir = os.getcwd() 87 | os.chdir(self.cacti_dir ) 88 | cmd = os.sep.join(['.','cacti -infile ']) + os.sep.join([self.results_dir,'cacti.cfg']) 89 | os.system( cmd) 90 | os.chdir(odir) 91 | 92 | -------------------------------------------------------------------------------- /scripts/utils/generate_verilog.py: -------------------------------------------------------------------------------- 1 | import os 2 | import math 3 | 4 | ################################################################################ 5 | # GENERATE VERILOG VIEW 6 | # 7 | # Generate a .v file based on the given SRAM. 8 | ################################################################################ 9 | 10 | def generate_verilog(mem, tmChkExpand=False): 11 | '''Generate a verilog view for the RAM''' 12 | name = str(mem.name) 13 | depth = int(mem.depth) 14 | bits = int(mem.width_in_bits) 15 | addr_width = math.ceil(math.log2(depth)) 16 | crpt_on_x = 1 17 | 18 | # Generate the 'setuphold' timing checks 19 | setuphold_checks = SH_LINE.format(sig=' we_in') 20 | setuphold_checks += SH_LINE.format(sig=' ce_in') 21 | if tmChkExpand: # per-bit checks 22 | for i in range(addr_width): setuphold_checks += SH_LINE.format(sig=f' addr_in[{i}]') 23 | for i in range( bits): setuphold_checks += SH_LINE.format(sig=f' wd_in[{i}]') 24 | for i in range( bits): setuphold_checks += SH_LINE.format(sig=f'w_mask_in[{i}]') 25 | else: # per-signal checks 26 | setuphold_checks += SH_LINE.format(sig=' addr_in') 27 | setuphold_checks += SH_LINE.format(sig=' wd_in') 28 | setuphold_checks += SH_LINE.format(sig=' w_mask_in') 29 | 30 | fout = os.sep.join([mem.results_dir, name + '.v']) 31 | with open(fout, 'w') as f: 32 | f.write(VLOG_TEMPLATE.format(name=name, data_width=bits, depth=depth, addr_width=addr_width, 33 | crpt_on_x=crpt_on_x, setuphold_checks=setuphold_checks)) 34 | 35 | def generate_verilog_bb( mem ): 36 | '''Generate a verilog black-box view for the RAM''' 37 | name = str(mem.name) 38 | depth = int(mem.depth) 39 | bits = int(mem.width_in_bits) 40 | addr_width = math.ceil(math.log2(depth)) 41 | crpt_on_x = 1 42 | 43 | fout = os.sep.join([mem.results_dir, name + '.bb.v']) 44 | with open(fout, 'w') as f: 45 | f.write(VLOG_BB_TEMPLATE.format(name=name, data_width=bits, depth=depth, addr_width=addr_width, 46 | crpt_on_x=crpt_on_x)) 47 | 48 | # Template line for a 'setuphold' time check 49 | SH_LINE = ' $setuphold (posedge clk, {sig}, 0, 0, notifier);\n' 50 | 51 | # Template for a verilog 1rw RAM model 52 | VLOG_TEMPLATE = '''\ 53 | module {name} 54 | ( 55 | rd_out, 56 | addr_in, 57 | we_in, 58 | wd_in, 59 | w_mask_in, 60 | clk, 61 | ce_in 62 | ); 63 | parameter BITS = {data_width}; 64 | parameter WORD_DEPTH = {depth}; 65 | parameter ADDR_WIDTH = {addr_width}; 66 | parameter corrupt_mem_on_X_p = {crpt_on_x}; 67 | 68 | output reg [BITS-1:0] rd_out; 69 | input [ADDR_WIDTH-1:0] addr_in; 70 | input we_in; 71 | input [BITS-1:0] wd_in; 72 | input [BITS-1:0] w_mask_in; 73 | input clk; 74 | input ce_in; 75 | 76 | reg [BITS-1:0] mem [0:WORD_DEPTH-1]; 77 | 78 | integer j; 79 | 80 | always @(posedge clk) 81 | begin 82 | if (ce_in) 83 | begin 84 | //if ((we_in !== 1'b1 && we_in !== 1'b0) && corrupt_mem_on_X_p) 85 | if (corrupt_mem_on_X_p && 86 | ((^we_in === 1'bx) || (^addr_in === 1'bx)) 87 | ) 88 | begin 89 | // WEN or ADDR is unknown, so corrupt entire array (using unsynthesizeable for loop) 90 | for (j = 0; j < WORD_DEPTH; j = j + 1) 91 | mem[j] <= 'x; 92 | $display("warning: ce_in=1, we_in is %b, addr_in = %x in fakeram_d64_w8", we_in, addr_in); 93 | end 94 | else if (we_in) 95 | begin 96 | mem[addr_in] <= (wd_in & w_mask_in) | (mem[addr_in] & ~w_mask_in); 97 | end 98 | // read 99 | rd_out <= mem[addr_in]; 100 | end 101 | else 102 | begin 103 | // Make sure read fails if ce_in is low 104 | rd_out <= 'x; 105 | end 106 | end 107 | 108 | // Timing check placeholders (will be replaced during SDF back-annotation) 109 | reg notifier; 110 | specify 111 | // Delay from clk to rd_out 112 | (posedge clk *> rd_out) = (0, 0); 113 | 114 | // Timing checks 115 | $width (posedge clk, 0, 0, notifier); 116 | $width (negedge clk, 0, 0, notifier); 117 | $period (posedge clk, 0, notifier); 118 | {setuphold_checks} 119 | endspecify 120 | 121 | endmodule 122 | ''' 123 | 124 | # Template for a verilog 1rw RAM interface 125 | VLOG_BB_TEMPLATE = '''\ 126 | module {name} 127 | ( 128 | rd_out, 129 | addr_in, 130 | we_in, 131 | wd_in, 132 | w_mask_in, 133 | clk, 134 | ce_in 135 | ); 136 | parameter BITS = {data_width}; 137 | parameter WORD_DEPTH = {depth}; 138 | parameter ADDR_WIDTH = {addr_width}; 139 | parameter corrupt_mem_on_X_p = {crpt_on_x}; 140 | 141 | output reg [BITS-1:0] rd_out; 142 | input [ADDR_WIDTH-1:0] addr_in; 143 | input we_in; 144 | input [BITS-1:0] wd_in; 145 | input [BITS-1:0] w_mask_in; 146 | input clk; 147 | input ce_in; 148 | 149 | endmodule 150 | ''' 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BSG Black-box SRAM Generator 2 | 3 | This project is desgined to generate black-boxed SRAMs for use in CAD flows 4 | where either an SRAM generator is not avaible or doesn't exist. 5 | 6 | ## Setup 7 | 8 | The black-box SRAM generator depends on lightly modified version of 9 | [Cacti](https://github.com/HewlettPackard/cacti) for area, power, and timing 10 | modeling. To build this version of Cacti, simply run: 11 | 12 | ``` 13 | $ make tools 14 | ``` 15 | 16 | ## Usage 17 | 18 | ### Configuration File 19 | 20 | The input to the BSG Black-box SRAM generator is a simple JSON file that 21 | contains some information about the technology node you are targeting as well 22 | as the size and names of SRAMs you would like to generate. Below is an example 23 | JSON file that can be found in `./example_cfgs/freepdk45.cfg`: 24 | 25 | ``` 26 | { 27 | "tech_nm": 45, 28 | "voltage": 1.1, 29 | "metalPrefix": "metal", 30 | "pinWidth_nm": 70, 31 | "pinPitch_nm": 140, 32 | "snapWidth_nm": 190, 33 | "snapHeight_nm": 1400, 34 | "flipPins": True, 35 | "srams": [ 36 | {"name": "sram_32x32_1rw", "width": 32, "depth": 32, "banks": 1}, 37 | {"name": "sram_8x512_1rw", "width": 8, "depth": 512, "banks": 1} 38 | ] 39 | } 40 | ``` 41 | 42 | `tech_nm` - The name of the target technology node (in nm). Used in Cacti for 43 | modeling PPA of the SRAM. 44 | 45 | `voltage` - Nominal operating voltage for the tech node. 46 | 47 | `metalPrefix` - The string that prefixes metal layers. 48 | 49 | `pinWidth_nm` - The width of the signal pins (in nm). 50 | 51 | `pinPitch_nm` - The minimum pin pitch for signal pins (in nm). All pins will 52 | have a pitch that is a multuple of this pitch. The first pin will be a 53 | multiple of this pitch from the bottom edge of the macro too. 54 | 55 | `snapWidth_nm` - (Optional : 1) Snap the width of the generated memory to a 56 | multiple of the given value. 57 | 58 | `snapHeight_nm` - (Optional : 1) Snap the height of the generated memory to a 59 | multiple of the given value. 60 | 61 | `flipPins` - (Optional : false) Flip the pins. If set to false then metal 1 is 62 | assumed to be vertical. This means that signal pins will be on metal 4 and the 63 | supply straps (also on metal 4) will be horizontal. If set to true then metal 1 64 | is assumed to be horizontal. This means that signal pins will be on metal 3 and 65 | the supply straps (on metal 4) will be vertical. 66 | 67 | `srams` - A list of SRAMs to generate. Each sram should have a `name`, `width` 68 | (or the number of bits per word), `depth` (or number of words), and `banks`. 69 | 70 | 71 | ### Running the Generator 72 | 73 | Now that you have a configuration file, it is time to run the generator. The 74 | main makefile target is: 75 | 76 | ``` 77 | $ make run CONFIG= 78 | ``` 79 | 80 | If you'd perfer, you can open up the Makefile and set `CONFIG` rather than 81 | setting it on the command line. 82 | 83 | All of the generated files can be found in the `./results` directory. Inside 84 | this directory will be a directory for each SRAM which contains the .lef, .lib 85 | and v file (as well as some intermediate files used for Cacti). 86 | 87 | ### Comparison with standard SRAMs generated with OpenRAM compiler 88 | 89 | #### Generated Fakerams (Eg:- fakeram130_1024x8) 90 | 91 | ![](docs/images/fakeram.png) 92 | 93 | ![](docs/images/fakeram_io.png) 94 | 95 | - The generated fakerams are 1rw RAMs 96 | - All pins are on the left side and they are all on Metal 3. 97 | - Pins: 98 | - 1x chip enable 99 | - 1x write enable 100 | - 1x clock 101 | - 1x address-in port 102 | - 1x data-in-data-out port 103 | - 1x write-mask-in port (bit masked). 104 | 105 | ![](docs/images/fakeram_power.png) 106 | 107 | - Power rails are vertical (can be made horizontal in the config file) - Alternate VDD and GND rails. 108 | - Metal layers 1, 2, 3 and 4 are blocked, metal 5 is free for routing over. 109 | 110 | #### Standard SRAMs compiled with OpenRAM (Eg:- [sky130_sram_1kbyte_1rw1r_8x1024_8](https://github.com/efabless/sky130_sram_macros/tree/main/sky130_sram_1kbyte_1rw1r_8x1024_8)) 111 | 112 | ![](docs/images/openram.png) 113 | 114 | ![](docs/images/openram_pins.png) 115 | 116 | - 1rw1r RAMs 117 | - Pins cover all 4 sides 118 | - I/O pins use Metal 3 (on left and right sides) & Metal 4 (on top and bottom sides) 119 | - Pins: 120 | - 2x clock 121 | - 2x chip select 122 | - 1x write enable 123 | - 2x address-in port 124 | - 1x data-out port 125 | - 1x data-in-data-out port 126 | - 1x write-mask pin/port (byte masked) 127 | 128 | - Power pins are in a ring format along the macro edge utilizing Metal 3 (Horizontal) & Metal 4 (Vertical) 129 | - Metal layers 1, 2, 3 and 4 are blocked, metal 5 is free for routing over. 130 | 131 | 132 | 133 | ## Feedback 134 | 135 | Feedback is always welcome! We ask that you submit a GitHub issue for any bugs, 136 | improvements, or new features you would like to see. We are also receptive to 137 | outside contributions but please be mindful of sensitive information that is 138 | commonly associated with licensed IP. 139 | 140 | -------------------------------------------------------------------------------- /patches/cacti.patch: -------------------------------------------------------------------------------- 1 | From 86fbd144d68b6874baff1572c3d0b2817d4461d3 Mon Sep 17 00:00:00 2001 2 | From: Scott Davidson 3 | Date: Tue, 7 Apr 2020 17:05:21 -0700 4 | Subject: [PATCH] Cacti patch 5 | 6 | --- 7 | io.cc | 94 +++++++++++++++++++++++++++++++++++-------------------------------- 8 | 1 file changed, 49 insertions(+), 45 deletions(-) 9 | 10 | diff --git a/io.cc b/io.cc 11 | index 3a798aa..e6037da 100644 12 | --- a/io.cc 13 | +++ b/io.cc 14 | @@ -2547,7 +2547,7 @@ void output_data_csv(const uca_org_t & fin_res, string fn) 15 | { 16 | file << "Tech node (nm), "; 17 | file << "Capacity (bytes), "; 18 | - file << "Number of banks, "; 19 | + //file << "Number of banks, "; 20 | file << "Associativity, "; 21 | file << "Output width (bits), "; 22 | file << "Access time (ns), "; 23 | @@ -2572,25 +2572,25 @@ void output_data_csv(const uca_org_t & fin_res, string fn) 24 | // file << "Leakage per bank with leak power management (mW), "; 25 | // file << "Refresh power as percentage of standby leakage, "; 26 | file << "Area (mm2), "; 27 | - file << "Ndwl, "; 28 | - file << "Ndbl, "; 29 | - file << "Nspd, "; 30 | - file << "Ndcm, "; 31 | - file << "Ndsam_level_1, "; 32 | - file << "Ndsam_level_2, "; 33 | - file << "Data arrary area efficiency %, "; 34 | - file << "Ntwl, "; 35 | - file << "Ntbl, "; 36 | - file << "Ntspd, "; 37 | - file << "Ntcm, "; 38 | - file << "Ntsam_level_1, "; 39 | - file << "Ntsam_level_2, "; 40 | - file << "Tag arrary area efficiency %, "; 41 | + //file << "Ndwl, "; 42 | + //file << "Ndbl, "; 43 | + //file << "Nspd, "; 44 | + //file << "Ndcm, "; 45 | + //file << "Ndsam_level_1, "; 46 | + //file << "Ndsam_level_2, "; 47 | + //file << "Data arrary area efficiency %, "; 48 | + //file << "Ntwl, "; 49 | + //file << "Ntbl, "; 50 | + //file << "Ntspd, "; 51 | + //file << "Ntcm, "; 52 | + //file << "Ntsam_level_1, "; 53 | + //file << "Ntsam_level_2, "; 54 | + //file << "Tag arrary area efficiency %, "; 55 | 56 | // file << "Resistance per unit micron (ohm-micron), "; 57 | // file << "Capacitance per unit micron (fF per micron), "; 58 | // file << "Unit-length wire delay (ps), "; 59 | -// file << "FO4 delay (ps), "; 60 | + file << "FO4 delay (ps), "; 61 | // file << "delay route to bank (including crossb delay) (ps), "; 62 | // file << "Crossbar delay (ps), "; 63 | // file << "Dyn read energy per access from closed page (nJ), "; 64 | @@ -2614,11 +2614,13 @@ void output_data_csv(const uca_org_t & fin_res, string fn) 65 | // file << "Delay opt (perc), "; 66 | // file << "Repeater opt (perc), "; 67 | // file << "Aspect ratio"; 68 | + file << "Width (um), "; 69 | + file << "Height (um), "; 70 | file << endl; 71 | } 72 | file << g_ip->F_sz_nm << ", "; 73 | file << g_ip->cache_sz << ", "; 74 | - file << g_ip->nbanks << ", "; 75 | + //file << g_ip->nbanks << ", "; 76 | file << g_ip->tag_assoc << ", "; 77 | file << g_ip->out_w << ", "; 78 | file << fin_res.access_time*1e+9 << ", "; 79 | @@ -2672,38 +2674,38 @@ void output_data_csv(const uca_org_t & fin_res, string fn) 80 | // file << fin_res.data_array.refresh_power / fin_res.data_array.total_power.readOp.leakage << ", "; 81 | file << fin_res.area*1e-6 << ", "; 82 | 83 | - file << fin_res.data_array2->Ndwl << ", "; 84 | - file << fin_res.data_array2->Ndbl << ", "; 85 | - file << fin_res.data_array2->Nspd << ", "; 86 | - file << fin_res.data_array2->deg_bl_muxing << ", "; 87 | - file << fin_res.data_array2->Ndsam_lev_1 << ", "; 88 | - file << fin_res.data_array2->Ndsam_lev_2 << ", "; 89 | - file << fin_res.data_array2->area_efficiency << ", "; 90 | - if (!(g_ip->fully_assoc || g_ip->pure_cam || g_ip->pure_ram)) 91 | - { 92 | - file << fin_res.tag_array2->Ndwl << ", "; 93 | - file << fin_res.tag_array2->Ndbl << ", "; 94 | - file << fin_res.tag_array2->Nspd << ", "; 95 | - file << fin_res.tag_array2->deg_bl_muxing << ", "; 96 | - file << fin_res.tag_array2->Ndsam_lev_1 << ", "; 97 | - file << fin_res.tag_array2->Ndsam_lev_2 << ", "; 98 | - file << fin_res.tag_array2->area_efficiency << ", "; 99 | - } 100 | - else 101 | - { 102 | - file << "N/A" << ", "; 103 | - file << "N/A"<< ", "; 104 | - file << "N/A" << ", "; 105 | - file << "N/A" << ", "; 106 | - file << "N/A" << ", "; 107 | - file << "N/A" << ", "; 108 | - file << "N/A" << ", "; 109 | - } 110 | + //file << fin_res.data_array2->Ndwl << ", "; 111 | + //file << fin_res.data_array2->Ndbl << ", "; 112 | + //file << fin_res.data_array2->Nspd << ", "; 113 | + //file << fin_res.data_array2->deg_bl_muxing << ", "; 114 | + //file << fin_res.data_array2->Ndsam_lev_1 << ", "; 115 | + //file << fin_res.data_array2->Ndsam_lev_2 << ", "; 116 | + //file << fin_res.data_array2->area_efficiency << ", "; 117 | + //if (!(g_ip->fully_assoc || g_ip->pure_cam || g_ip->pure_ram)) 118 | + //{ 119 | + //file << fin_res.tag_array2->Ndwl << ", "; 120 | + //file << fin_res.tag_array2->Ndbl << ", "; 121 | + //file << fin_res.tag_array2->Nspd << ", "; 122 | + //file << fin_res.tag_array2->deg_bl_muxing << ", "; 123 | + //file << fin_res.tag_array2->Ndsam_lev_1 << ", "; 124 | + //file << fin_res.tag_array2->Ndsam_lev_2 << ", "; 125 | + //file << fin_res.tag_array2->area_efficiency << ", "; 126 | + //} 127 | + //else 128 | + //{ 129 | + // file << "N/A" << ", "; 130 | + // file << "N/A"<< ", "; 131 | + // file << "N/A" << ", "; 132 | + // file << "N/A" << ", "; 133 | + // file << "N/A" << ", "; 134 | + // file << "N/A" << ", "; 135 | + // file << "N/A" << ", "; 136 | + //} 137 | 138 | // file << g_tp.wire_inside_mat.R_per_um << ", "; 139 | // file << g_tp.wire_inside_mat.C_per_um / 1e-15 << ", "; 140 | // file << g_tp.unit_len_wire_del / 1e-12 << ", "; 141 | -// file << g_tp.FO4 / 1e-12 << ", "; 142 | + file << g_tp.FO4 / 1e-12 << ", "; 143 | // file << fin_res.data_array.delay_route_to_bank / 1e-9 << ", "; 144 | // file << fin_res.data_array.delay_crossbar / 1e-9 << ", "; 145 | // file << fin_res.data_array.dyn_read_energy_from_closed_page / 1e-9 << ", "; 146 | @@ -2721,6 +2723,8 @@ void output_data_csv(const uca_org_t & fin_res, string fn) 147 | // file << fin_res.data_array.cas_latency * 1e9 << ", " ; 148 | // file << fin_res.data_array.precharge_delay * 1e9 << ", " ; 149 | // file << fin_res.data_array.all_banks_height / fin_res.data_array.all_banks_width; 150 | + file << fin_res.cache_ht << ", "; 151 | + file << fin_res.cache_len << ", "; 152 | file< 0: 52 | track_count += 1 53 | number_of_spare_tracks = number_of_tracks_available - number_of_pins*track_count 54 | track_count -= 1 55 | 56 | pin_pitch = min_pin_pitch * track_count 57 | group_pitch = math.floor((number_of_tracks_available - number_of_pins*track_count) / 4)*mem.process.pinPitch_um 58 | 59 | ######################################### 60 | # LEF HEADER 61 | ######################################### 62 | 63 | fid.write('VERSION 5.7 ;\n') 64 | fid.write('BUSBITCHARS "[]" ;\n') 65 | fid.write('MACRO %s\n' % (name)) 66 | fid.write(' FOREIGN %s 0 0 ;\n' % (name)) 67 | fid.write(' SYMMETRY X Y R90 ;\n') 68 | fid.write(' SIZE %.3f BY %.3f ;\n' % (w,h)) 69 | fid.write(' CLASS BLOCK ;\n') 70 | 71 | ######################################## 72 | # LEF SIGNAL PINS 73 | ######################################## 74 | 75 | y_step = y_offset 76 | for i in range(int(bits)) : 77 | y_step = lef_add_pin( fid, mem, 'w_mask_in[%d]'%i, True, y_step, pin_pitch ) 78 | 79 | y_step += group_pitch-pin_pitch 80 | for i in range(int(bits)) : 81 | y_step = lef_add_pin( fid, mem, 'rd_out[%d]'%i, False, y_step, pin_pitch ) 82 | 83 | y_step += group_pitch-pin_pitch 84 | for i in range(int(bits)) : 85 | y_step = lef_add_pin( fid, mem, 'wd_in[%d]'%i, True, y_step, pin_pitch ) 86 | 87 | y_step += group_pitch-pin_pitch 88 | for i in range(int(addr_width)) : 89 | y_step = lef_add_pin( fid, mem, 'addr_in[%d]'%i, True, y_step, pin_pitch ) 90 | 91 | y_step += group_pitch-pin_pitch 92 | y_step = lef_add_pin( fid, mem, 'we_in', True, y_step, pin_pitch ) 93 | y_step = lef_add_pin( fid, mem, 'ce_in', True, y_step, pin_pitch ) 94 | y_step = lef_add_pin( fid, mem, 'clk', True, y_step, pin_pitch ) 95 | 96 | ######################################## 97 | # Create VDD/VSS Strapes 98 | ######################################## 99 | 100 | supply_pin_width = min_pin_width*4 101 | supply_pin_half_width = supply_pin_width/2 102 | supply_pin_pitch = min_pin_pitch*8 103 | supply_pin_layer = '%s4' % metalPrefix 104 | 105 | # Vertical straps 106 | if flip: 107 | x_step = x_offset 108 | fid.write(' PIN VSS\n') 109 | fid.write(' DIRECTION INOUT ;\n') 110 | fid.write(' USE GROUND ;\n') 111 | fid.write(' PORT\n') 112 | fid.write(' LAYER %s ;\n' % supply_pin_layer) 113 | while x_step <= w - x_offset: 114 | fid.write(' RECT %.3f %.3f %.3f %.3f ;\n' % (x_step-supply_pin_half_width, y_offset, x_step+supply_pin_half_width, h-y_offset)) 115 | x_step += supply_pin_pitch*2 116 | fid.write(' END\n') 117 | fid.write(' END VSS\n') 118 | 119 | x_step = x_offset + supply_pin_pitch 120 | fid.write(' PIN VDD\n') 121 | fid.write(' DIRECTION INOUT ;\n') 122 | fid.write(' USE POWER ;\n') 123 | fid.write(' PORT\n') 124 | fid.write(' LAYER %s ;\n' % supply_pin_layer) 125 | while x_step <= w - x_offset: 126 | fid.write(' RECT %.3f %.3f %.3f %.3f ;\n' % (x_step-supply_pin_half_width, y_offset, x_step+supply_pin_half_width, h-y_offset)) 127 | x_step += supply_pin_pitch*2 128 | fid.write(' END\n') 129 | fid.write(' END VDD\n') 130 | 131 | # Horizontal straps 132 | else: 133 | y_step = y_offset 134 | fid.write(' PIN VSS\n') 135 | fid.write(' DIRECTION INOUT ;\n') 136 | fid.write(' USE GROUND ;\n') 137 | fid.write(' PORT\n') 138 | fid.write(' LAYER %s ;\n' % supply_pin_layer) 139 | while y_step <= h - y_offset: 140 | fid.write(' RECT %.3f %.3f %.3f %.3f ;\n' % (x_offset, y_step-supply_pin_half_width, w-x_offset, y_step+supply_pin_half_width)) 141 | y_step += supply_pin_pitch*2 142 | fid.write(' END\n') 143 | fid.write(' END VSS\n') 144 | 145 | y_step = y_offset + supply_pin_pitch 146 | fid.write(' PIN VDD\n') 147 | fid.write(' DIRECTION INOUT ;\n') 148 | fid.write(' USE POWER ;\n') 149 | fid.write(' PORT\n') 150 | fid.write(' LAYER %s ;\n' % supply_pin_layer) 151 | while y_step <= h - y_offset: 152 | fid.write(' RECT %.3f %.3f %.3f %.3f ;\n' % (x_offset, y_step-supply_pin_half_width, w-x_offset, y_step+supply_pin_half_width)) 153 | y_step += supply_pin_pitch*2 154 | fid.write(' END\n') 155 | fid.write(' END VDD\n') 156 | 157 | ######################################## 158 | # Create obstructions 159 | ######################################## 160 | 161 | fid.write(' OBS\n') 162 | 163 | ################ 164 | # Layer 1 165 | ################ 166 | 167 | # No pins (full rect) 168 | fid.write(' LAYER %s1 ;\n' % metalPrefix) 169 | fid.write(' RECT 0 0 %.3f %.3f ;\n' % (w,h)) 170 | 171 | ################ 172 | # Layer 2 173 | ################ 174 | 175 | # No pins (full rect) 176 | fid.write(' LAYER %s2 ;\n' % metalPrefix) 177 | fid.write(' RECT 0 0 %.3f %.3f ;\n' % (w,h)) 178 | 179 | ################ 180 | # Layer 3 181 | ################ 182 | 183 | fid.write(' LAYER %s3 ;\n' % metalPrefix) 184 | 185 | # Flipped therefore pins on M3 186 | if flip: 187 | 188 | # Rect from top to bottom, just right of pins to right edge 189 | fid.write(' RECT %.3f 0 %.3f %.3f ;\n' % (pin_height,w,h)) 190 | 191 | # Walk through same calculation as pins and draw from bottom of the 192 | # current pin to the top of last pin (start with bottom edge) 193 | prev_y = 0 194 | y_step = y_offset 195 | for i in range(int(bits)) : 196 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (prev_y,pin_height,y_step-min_pin_width/2)) 197 | prev_y = y_step+min_pin_width/2 198 | y_step += pin_pitch 199 | y_step += group_pitch-pin_pitch 200 | for i in range(int(bits)) : 201 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (prev_y,pin_height,y_step-min_pin_width/2)) 202 | prev_y = y_step+min_pin_width/2 203 | y_step += pin_pitch 204 | y_step += group_pitch-pin_pitch 205 | for i in range(int(bits)) : 206 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (prev_y,pin_height,y_step-min_pin_width/2)) 207 | prev_y = y_step+min_pin_width/2 208 | y_step += pin_pitch 209 | y_step += group_pitch-pin_pitch 210 | for i in range(int(addr_width)) : 211 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (prev_y,pin_height,y_step-min_pin_width/2)) 212 | prev_y = y_step+min_pin_width/2 213 | y_step += pin_pitch 214 | y_step += group_pitch-pin_pitch 215 | for i in range(3): 216 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (prev_y,pin_height,y_step-min_pin_width/2)) 217 | prev_y = y_step+min_pin_width/2 218 | y_step += pin_pitch 219 | 220 | # Final shapre from top of last pin to top edge 221 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (prev_y,pin_height,h)) 222 | 223 | # Not flipped therefore no pins on M3 (Full rect) 224 | else: 225 | fid.write(' RECT 0 0 %.3f %.3f ;\n' % (w,h)) 226 | 227 | ################ 228 | # Layer 4 229 | ################ 230 | 231 | fid.write(' LAYER %s4 ;\n' % metalPrefix) 232 | 233 | # Flipped therefore only vertical pg straps 234 | if flip: 235 | 236 | # Block under and above the vertical power straps (full width) 237 | fid.write(' RECT 0 0 %.3f %.3f ;\n' % (w, y_offset)) 238 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (h-y_offset,w,h)) 239 | 240 | # Walk through the same calculations to create pg straps and create obs 241 | # from the left of the current strap to the right of the previous strap 242 | # (start with the left edge) 243 | prev_x = 0 244 | x_step = x_offset 245 | while x_step <= w - x_offset: 246 | fid.write(' RECT %.3f %.3f %.3f %.3f ;\n' % (prev_x,y_offset,x_step-supply_pin_half_width,h-y_offset)) 247 | prev_x = x_step+supply_pin_half_width 248 | x_step += supply_pin_pitch 249 | 250 | # Create a block from the right of the last strap to the right edge 251 | fid.write(' RECT %.3f %.3f %.3f %.3f ;\n' % (prev_x,y_offset,w,h-y_offset)) 252 | 253 | # Not flipped therefore pins on M4 and horizontal pg straps 254 | else: 255 | 256 | # Block from right of pins to left of straps and a block to the right 257 | # of the straps (full height) 258 | fid.write(' RECT %.3f 0 %.3f %.3f ;\n' % (min_pin_width, x_offset, h)) 259 | fid.write(' RECT %.3f 0 %.3f %.3f ;\n' % (w-x_offset, w, h)) 260 | 261 | # Walk through the same calculations to create pg straps and create obs 262 | # from the bottom of the current strap to the top of the previous strap 263 | # (start with the bottom edge) 264 | prev_y = 0 265 | y_step = y_offset 266 | while y_step <= h - y_offset: 267 | fid.write(' RECT %.3f %.3f %.3f %.3f ;\n' % (x_offset, prev_y, w-x_offset, y_step-supply_pin_half_width)) 268 | prev_y = y_step+supply_pin_half_width 269 | y_step += supply_pin_pitch 270 | 271 | # Create a block from the top of the last strap to the top edge 272 | fid.write(' RECT %.3f %.3f %.3f %.3f ;\n' % (x_offset, prev_y, w-x_offset, h)) 273 | 274 | # Walk through same calculation as pins and draw from bottom of the 275 | # current pin to the top of last pin (start with bottom edge) 276 | prev_y = 0 277 | y_step = y_offset 278 | for i in range(int(bits)) : 279 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (prev_y,min_pin_width,y_step-min_pin_width/2)) 280 | prev_y = y_step+min_pin_width/2 281 | y_step += pin_pitch 282 | y_step += group_pitch-pin_pitch 283 | for i in range(int(bits)) : 284 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (prev_y,min_pin_width,y_step-min_pin_width/2)) 285 | prev_y = y_step+min_pin_width/2 286 | y_step += pin_pitch 287 | y_step += group_pitch-pin_pitch 288 | for i in range(int(bits)) : 289 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (prev_y,min_pin_width,y_step-min_pin_width/2)) 290 | prev_y = y_step+min_pin_width/2 291 | y_step += pin_pitch 292 | y_step += group_pitch-pin_pitch 293 | for i in range(int(addr_width)) : 294 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (prev_y,min_pin_width,y_step-min_pin_width/2)) 295 | prev_y = y_step+min_pin_width/2 296 | y_step += pin_pitch 297 | y_step += group_pitch-pin_pitch 298 | for i in range(3): 299 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (prev_y,min_pin_width,y_step-min_pin_width/2)) 300 | prev_y = y_step+min_pin_width/2 301 | y_step += pin_pitch 302 | 303 | # Final shapre from top of last pin to top edge 304 | fid.write(' RECT 0 %.3f %.3f %.3f ;\n' % (prev_y,min_pin_width,h)) 305 | 306 | # Overlap layer (full rect) 307 | fid.write(' LAYER OVERLAP ;\n') 308 | fid.write(' RECT 0 0 %.3f %.3f ;\n' % (w,h)) 309 | 310 | # Finish up LEF file 311 | fid.write(' END\n') 312 | fid.write('END %s\n' % name) 313 | fid.write('\n') 314 | fid.write('END LIBRARY\n') 315 | fid.close() 316 | 317 | # 318 | # Helper function that adds a signal pin 319 | # 320 | def lef_add_pin( fid, mem, pin_name, is_input, y, pitch ): 321 | 322 | layer = mem.process.metalPrefix + ('3' if mem.process.flipPins.lower() == 'true' else '4') 323 | pw = mem.process.pinWidth_um 324 | hpw = (mem.process.pinWidth_um/2.0) # half pin width 325 | ph = mem.process.pinHeight_um 326 | 327 | fid.write(' PIN %s\n' % pin_name) 328 | fid.write(' DIRECTION %s ;\n' % ('INPUT' if is_input else 'OUTPUT')) 329 | fid.write(' USE SIGNAL ;\n') 330 | fid.write(' SHAPE ABUTMENT ;\n') 331 | fid.write(' PORT\n') 332 | fid.write(' LAYER %s ;\n' % layer) 333 | fid.write(' RECT %.3f %.3f %.3f %.3f ;\n' % (0, y-hpw, ph, y+hpw)) 334 | fid.write(' END\n') 335 | fid.write(' END %s\n' % pin_name) 336 | 337 | return y + pitch 338 | 339 | -------------------------------------------------------------------------------- /scripts/utils/generate_lib.py: -------------------------------------------------------------------------------- 1 | import os 2 | import math 3 | import time 4 | import datetime 5 | 6 | ################################################################################ 7 | # GENERATE LIBERTY VIEW 8 | # 9 | # Generate a .lib file based on the given SRAM. 10 | ################################################################################ 11 | 12 | def generate_lib( mem ): 13 | 14 | # Make sure the data types are correct 15 | name = str(mem.name) 16 | depth = int(mem.depth) 17 | bits = int(mem.width_in_bits) 18 | area = float(mem.area_um2) 19 | x = float(mem.width_um) 20 | y = float(mem.height_um) 21 | leakage = float(mem.standby_leakage_per_bank_mW)*1e3 22 | tsetup = float(mem.t_setup_ns) 23 | thold = float(mem.t_hold_ns) 24 | tcq = float(mem.access_time_ns) 25 | clkpindynamic = float(mem.pin_dynamic_power_mW)*1e3 26 | pindynamic = float(mem.pin_dynamic_power_mW)*1e1 27 | min_driver_in_cap = float(mem.cap_input_pf) 28 | voltage = float(mem.process.voltage) 29 | min_period = float(mem.cycle_time_ns) 30 | fo4 = float(mem.fo4_ps)/1e3 31 | 32 | # Only support 1RW srams. At some point, expose these as well! 33 | num_rwport = mem.rw_ports 34 | 35 | # Number of bits for address 36 | addr_width = math.ceil(math.log2(mem.depth)) 37 | addr_width_m1 = addr_width-1 38 | 39 | # Get the date 40 | d = datetime.date.today() 41 | date = d.isoformat() 42 | current_time = time.strftime("%H:%M:%SZ", time.gmtime()) 43 | 44 | # TODO: Arbitrary indicies for the NLDM table. This is used for Clk->Q arcs 45 | # as well as setup/hold times. We only have a single value for these, there 46 | # are two options. 1. adding some sort of static variation of the single 47 | # value for each table entry, 2. use the same value so all interpolated 48 | # values are the same. The 1st is more realistic but depend on good variation 49 | # values which is process sepcific and I don't have a strategy for 50 | # determining decent variation values without breaking NDA so right now we 51 | # have no variations. 52 | # 53 | # The table indicies are main min/max values for interpolation. The tools 54 | # typically don't like extrapolation so a large range is nice, but makes the 55 | # single value strategy described above even more unrealistic. 56 | # 57 | min_slew = 1 * fo4 ;# arbitrary (1x fo4, fear that 0 would cause issues) 58 | max_slew = 25 * fo4 ;# arbitrary (25x fo4 as ~100x fanout ... i know that is not really how it works) 59 | min_load = 1 * min_driver_in_cap ;# arbitrary (1x driver, fear that 0 would cause issues) 60 | max_load = 100 * min_driver_in_cap ;# arbitrary (100x driver) 61 | 62 | slew_indicies = '%.3f, %.3f' % (min_slew, max_slew) ;# input pin transisiton with between 1xfo4 and 100xfo4 63 | load_indicies = '%.3f, %.3f' % (min_load, max_load) ;# output capacitance table between a 1x and 32x inverter 64 | 65 | # Start generating the LIB file 66 | 67 | LIB_file = open(os.sep.join([mem.results_dir, name + '.lib']), 'w') 68 | 69 | LIB_file.write( 'library(%s) {\n' % name) 70 | LIB_file.write( ' technology (cmos);\n') 71 | LIB_file.write( ' delay_model : table_lookup;\n') 72 | LIB_file.write( ' revision : 1.0;\n') 73 | LIB_file.write( ' date : "%s %s";\n' % (date, current_time)) 74 | LIB_file.write( ' comment : "SRAM";\n') 75 | LIB_file.write( ' time_unit : "1ns";\n') 76 | LIB_file.write( ' voltage_unit : "1V";\n') 77 | LIB_file.write( ' current_unit : "1uA";\n') 78 | LIB_file.write( ' leakage_power_unit : "1uW";\n') 79 | LIB_file.write( ' nom_process : 1;\n') 80 | LIB_file.write( ' nom_temperature : 25.000;\n') 81 | LIB_file.write( ' nom_voltage : %s;\n' % voltage) 82 | LIB_file.write( ' capacitive_load_unit (1,pf);\n\n') 83 | LIB_file.write( ' pulling_resistance_unit : "1kohm";\n\n') 84 | LIB_file.write( ' operating_conditions(tt_1.0_25.0) {\n') 85 | LIB_file.write( ' process : 1;\n') 86 | LIB_file.write( ' temperature : 25.000;\n') 87 | LIB_file.write( ' voltage : %s;\n' % voltage) 88 | LIB_file.write( ' tree_type : balanced_tree;\n') 89 | LIB_file.write( ' }\n') 90 | LIB_file.write( '\n') 91 | 92 | LIB_file.write( ' /* default attributes */\n') 93 | LIB_file.write( ' default_cell_leakage_power : 0;\n') 94 | LIB_file.write( ' default_fanout_load : 1;\n') 95 | LIB_file.write( ' default_inout_pin_cap : 0.0;\n') 96 | LIB_file.write( ' default_input_pin_cap : 0.0;\n') 97 | LIB_file.write( ' default_output_pin_cap : 0.0;\n') 98 | LIB_file.write( ' default_input_pin_cap : 0.0;\n') 99 | LIB_file.write( ' default_max_transition : %.3f;\n\n' % max_slew) 100 | LIB_file.write( ' default_operating_conditions : tt_1.0_25.0;\n') 101 | LIB_file.write( ' default_leakage_power_density : 0.0;\n') 102 | LIB_file.write( '\n') 103 | 104 | LIB_file.write( ' /* additional header data */\n') 105 | LIB_file.write( ' slew_derate_from_library : 1.000;\n') 106 | LIB_file.write( ' slew_lower_threshold_pct_fall : 20.000;\n') 107 | LIB_file.write( ' slew_upper_threshold_pct_fall : 80.000;\n') 108 | LIB_file.write( ' slew_lower_threshold_pct_rise : 20.000;\n') 109 | LIB_file.write( ' slew_upper_threshold_pct_rise : 80.000;\n') 110 | LIB_file.write( ' input_threshold_pct_fall : 50.000;\n') 111 | LIB_file.write( ' input_threshold_pct_rise : 50.000;\n') 112 | LIB_file.write( ' output_threshold_pct_fall : 50.000;\n') 113 | LIB_file.write( ' output_threshold_pct_rise : 50.000;\n\n') 114 | LIB_file.write( '\n') 115 | 116 | # LIB_file.write( ' /* k-factors */\n') 117 | # LIB_file.write( ' k_process_cell_fall : 1;\n') 118 | # LIB_file.write( ' k_process_cell_leakage_power : 0;\n') 119 | # LIB_file.write( ' k_process_cell_rise : 1;\n') 120 | # LIB_file.write( ' k_process_fall_transition : 1;\n') 121 | # LIB_file.write( ' k_process_hold_fall : 1;\n') 122 | # LIB_file.write( ' k_process_hold_rise : 1;\n') 123 | # LIB_file.write( ' k_process_internal_power : 0;\n') 124 | # LIB_file.write( ' k_process_min_pulse_width_high : 1;\n') 125 | # LIB_file.write( ' k_process_min_pulse_width_low : 1;\n') 126 | # LIB_file.write( ' k_process_pin_cap : 0;\n') 127 | # LIB_file.write( ' k_process_recovery_fall : 1;\n') 128 | # LIB_file.write( ' k_process_recovery_rise : 1;\n') 129 | # LIB_file.write( ' k_process_rise_transition : 1;\n') 130 | # LIB_file.write( ' k_process_setup_fall : 1;\n') 131 | # LIB_file.write( ' k_process_setup_rise : 1;\n') 132 | # LIB_file.write( ' k_process_wire_cap : 0;\n') 133 | # LIB_file.write( ' k_process_wire_res : 0;\n') 134 | # LIB_file.write( ' k_temp_cell_fall : 0.000;\n') 135 | # LIB_file.write( ' k_temp_cell_rise : 0.000;\n') 136 | # LIB_file.write( ' k_temp_hold_fall : 0.000;\n') 137 | # LIB_file.write( ' k_temp_hold_rise : 0.000;\n') 138 | # LIB_file.write( ' k_temp_min_pulse_width_high : 0.000;\n') 139 | # LIB_file.write( ' k_temp_min_pulse_width_low : 0.000;\n') 140 | # LIB_file.write( ' k_temp_min_period : 0.000;\n') 141 | # LIB_file.write( ' k_temp_rise_propagation : 0.000;\n') 142 | # LIB_file.write( ' k_temp_fall_propagation : 0.000;\n') 143 | # LIB_file.write( ' k_temp_rise_transition : 0.0;\n') 144 | # LIB_file.write( ' k_temp_fall_transition : 0.0;\n') 145 | # LIB_file.write( ' k_temp_recovery_fall : 0.000;\n') 146 | # LIB_file.write( ' k_temp_recovery_rise : 0.000;\n') 147 | # LIB_file.write( ' k_temp_setup_fall : 0.000;\n') 148 | # LIB_file.write( ' k_temp_setup_rise : 0.000;\n') 149 | # LIB_file.write( ' k_volt_cell_fall : 0.000;\n') 150 | # LIB_file.write( ' k_volt_cell_rise : 0.000;\n') 151 | # LIB_file.write( ' k_volt_hold_fall : 0.000;\n') 152 | # LIB_file.write( ' k_volt_hold_rise : 0.000;\n') 153 | # LIB_file.write( ' k_volt_min_pulse_width_high : 0.000;\n') 154 | # LIB_file.write( ' k_volt_min_pulse_width_low : 0.000;\n') 155 | # LIB_file.write( ' k_volt_min_period : 0.000;\n') 156 | # LIB_file.write( ' k_volt_rise_propagation : 0.000;\n') 157 | # LIB_file.write( ' k_volt_fall_propagation : 0.000;\n') 158 | # LIB_file.write( ' k_volt_rise_transition : 0.0;\n') 159 | # LIB_file.write( ' k_volt_fall_transition : 0.0;\n') 160 | # LIB_file.write( ' k_volt_recovery_fall : 0.000;\n') 161 | # LIB_file.write( ' k_volt_recovery_rise : 0.000;\n') 162 | # LIB_file.write( ' k_volt_setup_fall : 0.000;\n') 163 | # LIB_file.write( ' k_volt_setup_rise : 0.000;\n') 164 | # LIB_file.write( '\n') 165 | 166 | LIB_file.write( ' lu_table_template(%s_mem_out_delay_template) {\n' % name ) 167 | LIB_file.write( ' variable_1 : input_net_transition;\n') 168 | LIB_file.write( ' variable_2 : total_output_net_capacitance;\n') 169 | LIB_file.write( ' index_1 ("1000, 1001");\n') 170 | LIB_file.write( ' index_2 ("1000, 1001");\n') 171 | LIB_file.write( ' }\n') 172 | LIB_file.write( ' lu_table_template(%s_mem_out_slew_template) {\n' % name ) 173 | LIB_file.write( ' variable_1 : total_output_net_capacitance;\n') 174 | LIB_file.write( ' index_1 ("1000, 1001");\n') 175 | LIB_file.write( ' }\n') 176 | LIB_file.write( ' lu_table_template(%s_constraint_template) {\n' % name ) 177 | LIB_file.write( ' variable_1 : related_pin_transition;\n') 178 | LIB_file.write( ' variable_2 : constrained_pin_transition;\n') 179 | LIB_file.write( ' index_1 ("1000, 1001");\n') 180 | LIB_file.write( ' index_2 ("1000, 1001");\n') 181 | LIB_file.write( ' }\n') 182 | LIB_file.write( ' power_lut_template(%s_energy_template_clkslew) {\n' % name ) 183 | LIB_file.write( ' variable_1 : input_transition_time;\n') 184 | LIB_file.write( ' index_1 ("1000, 1001");\n') 185 | LIB_file.write( ' }\n') 186 | LIB_file.write( ' power_lut_template(%s_energy_template_sigslew) {\n' % name ) 187 | LIB_file.write( ' variable_1 : input_transition_time;\n') 188 | LIB_file.write( ' index_1 ("1000, 1001");\n') 189 | LIB_file.write( ' }\n') 190 | LIB_file.write( ' library_features(report_delay_calculation);\n') 191 | LIB_file.write( ' type (%s_DATA) {\n' % name ) 192 | LIB_file.write( ' base_type : array ;\n') 193 | LIB_file.write( ' data_type : bit ;\n') 194 | LIB_file.write( ' bit_width : %d;\n' % bits) 195 | LIB_file.write( ' bit_from : %d;\n' % (int(bits)-1)) 196 | LIB_file.write( ' bit_to : 0 ;\n') 197 | LIB_file.write( ' downto : true ;\n') 198 | LIB_file.write( ' }\n') 199 | LIB_file.write( ' type (%s_ADDRESS) {\n' % name) 200 | LIB_file.write( ' base_type : array ;\n') 201 | LIB_file.write( ' data_type : bit ;\n') 202 | LIB_file.write( ' bit_width : %d;\n' % addr_width) 203 | LIB_file.write( ' bit_from : %d;\n' % addr_width_m1) 204 | LIB_file.write( ' bit_to : 0 ;\n') 205 | LIB_file.write( ' downto : true ;\n') 206 | LIB_file.write( ' }\n') 207 | 208 | LIB_file.write( 'cell(%s) {\n' % name ) 209 | LIB_file.write( ' area : %.3f;\n' % area) 210 | #LIB_file.write( ' dont_use : true;\n') 211 | #LIB_file.write( ' dont_touch : true;\n') 212 | LIB_file.write( ' interface_timing : true;\n') 213 | LIB_file.write( ' memory() {\n') 214 | LIB_file.write( ' type : ram;\n') 215 | LIB_file.write( ' address_width : %d;\n' % addr_width) 216 | LIB_file.write( ' word_width : %d;\n' % bits) 217 | LIB_file.write( ' }\n') 218 | 219 | LIB_file.write(' pin(clk) {\n') 220 | LIB_file.write(' direction : input;\n') 221 | LIB_file.write(' capacitance : %.3f;\n' % (min_driver_in_cap*5)) ;# Clk pin is usually higher cap for fanout control, assuming an x5 driver. 222 | LIB_file.write(' clock : true;\n') 223 | #LIB_file.write(' max_transition : 0.01;\n') # Max rise/fall time 224 | #LIB_file.write(' min_pulse_width_high : %.3f ;\n' % (min_period)) 225 | #LIB_file.write(' min_pulse_width_low : %.3f ;\n' % (min_period)) 226 | LIB_file.write(' min_period : %.3f ;\n' % (min_period)) 227 | #LIB_file.write(' minimum_period(){\n') 228 | #LIB_file.write(' constraint : %.3f ;\n' % min_period) 229 | #LIB_file.write(' when : "1";\n') 230 | #LIB_file.write(' sdf_cond : "1";\n') 231 | #LIB_file.write(' }\n') 232 | LIB_file.write(' internal_power(){\n') 233 | LIB_file.write(' rise_power(%s_energy_template_clkslew) {\n' % name) 234 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 235 | LIB_file.write(' values ("%.3f, %.3f")\n' % (clkpindynamic, clkpindynamic)) 236 | LIB_file.write(' }\n') 237 | LIB_file.write(' fall_power(%s_energy_template_clkslew) {\n' % name) 238 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 239 | LIB_file.write(' values ("%.3f, %.3f")\n' % (clkpindynamic, clkpindynamic)) 240 | LIB_file.write(' }\n') 241 | LIB_file.write(' }\n') 242 | LIB_file.write(' }\n') 243 | LIB_file.write('\n') 244 | 245 | for i in range(int(num_rwport)) : 246 | LIB_file.write(' bus(rd_out) {\n') 247 | LIB_file.write(' bus_type : %s_DATA;\n' % name) 248 | LIB_file.write(' direction : output;\n') 249 | LIB_file.write(' max_capacitance : %.3f;\n' % max_load) ;# Based on 32x inverter being a common max (or near max) inverter 250 | LIB_file.write(' memory_read() {\n') 251 | LIB_file.write(' address : addr_in;\n') 252 | LIB_file.write(' }\n') 253 | LIB_file.write(' timing() {\n') 254 | LIB_file.write(' related_pin : "clk" ;\n') 255 | LIB_file.write(' timing_type : rising_edge;\n') 256 | LIB_file.write(' timing_sense : non_unate;\n') 257 | LIB_file.write(' cell_rise(%s_mem_out_delay_template) {\n' % name) 258 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 259 | LIB_file.write(' index_2 ("%s");\n' % load_indicies) 260 | LIB_file.write(' values ( \\\n') 261 | LIB_file.write(' "%.3f, %.3f", \\\n' % (tcq, tcq)) 262 | LIB_file.write(' "%.3f, %.3f" \\\n' % (tcq, tcq)) 263 | LIB_file.write(' )\n') 264 | LIB_file.write(' }\n') 265 | LIB_file.write(' cell_fall(%s_mem_out_delay_template) {\n' % name) 266 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 267 | LIB_file.write(' index_2 ("%s");\n' % load_indicies) 268 | LIB_file.write(' values ( \\\n') 269 | LIB_file.write(' "%.3f, %.3f", \\\n' % (tcq, tcq)) 270 | LIB_file.write(' "%.3f, %.3f" \\\n' % (tcq, tcq)) 271 | LIB_file.write(' )\n') 272 | LIB_file.write(' }\n') 273 | LIB_file.write(' rise_transition(%s_mem_out_slew_template) {\n' % name) 274 | LIB_file.write(' index_1 ("%s");\n' % load_indicies) 275 | LIB_file.write(' values ("%.3f, %.3f")\n' % (min_slew, max_slew)) 276 | LIB_file.write(' }\n') 277 | LIB_file.write(' fall_transition(%s_mem_out_slew_template) {\n' % name) 278 | LIB_file.write(' index_1 ("%s");\n' % load_indicies) 279 | LIB_file.write(' values ("%.3f, %.3f")\n' % (min_slew, max_slew)) 280 | LIB_file.write(' }\n') 281 | LIB_file.write(' }\n') 282 | LIB_file.write(' }\n') 283 | 284 | for i in range(int(num_rwport)) : 285 | LIB_file.write(' pin(we_in){\n') 286 | LIB_file.write(' direction : input;\n') 287 | LIB_file.write(' capacitance : %.3f;\n' % (min_driver_in_cap)) 288 | LIB_file.write(' timing() {\n') 289 | LIB_file.write(' related_pin : clk;\n') 290 | LIB_file.write(' timing_type : setup_rising ;\n') 291 | LIB_file.write(' rise_constraint(%s_constraint_template) {\n' % name) 292 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 293 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 294 | LIB_file.write(' values ( \\\n') 295 | LIB_file.write(' "%.3f, %.3f", \\\n' % (tsetup, tsetup)) 296 | LIB_file.write(' "%.3f, %.3f" \\\n' % (tsetup, tsetup)) 297 | LIB_file.write(' )\n') 298 | LIB_file.write(' }\n') 299 | LIB_file.write(' fall_constraint(%s_constraint_template) {\n' % name) 300 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 301 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 302 | LIB_file.write(' values ( \\\n') 303 | LIB_file.write(' "%.3f, %.3f", \\\n' % (tsetup, tsetup)) 304 | LIB_file.write(' "%.3f, %.3f" \\\n' % (tsetup, tsetup)) 305 | LIB_file.write(' )\n') 306 | LIB_file.write(' }\n') 307 | LIB_file.write(' } \n') 308 | LIB_file.write(' timing() {\n') 309 | LIB_file.write(' related_pin : clk;\n') 310 | LIB_file.write(' timing_type : hold_rising ;\n') 311 | LIB_file.write(' rise_constraint(%s_constraint_template) {\n' % name) 312 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 313 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 314 | LIB_file.write(' values ( \\\n') 315 | LIB_file.write(' "%.3f, %.3f", \\\n' % (thold, thold)) 316 | LIB_file.write(' "%.3f, %.3f" \\\n' % (thold, thold)) 317 | LIB_file.write(' )\n') 318 | LIB_file.write(' }\n') 319 | LIB_file.write(' fall_constraint(%s_constraint_template) {\n' % name) 320 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 321 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 322 | LIB_file.write(' values ( \\\n') 323 | LIB_file.write(' "%.3f, %.3f", \\\n' % (thold, thold)) 324 | LIB_file.write(' "%.3f, %.3f" \\\n' % (thold, thold)) 325 | LIB_file.write(' )\n') 326 | LIB_file.write(' }\n') 327 | LIB_file.write(' }\n') 328 | LIB_file.write(' internal_power(){\n') 329 | LIB_file.write(' rise_power(%s_energy_template_sigslew) {\n' % name) 330 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 331 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 332 | LIB_file.write(' }\n') 333 | LIB_file.write(' fall_power(%s_energy_template_sigslew) {\n' % name) 334 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 335 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 336 | LIB_file.write(' }\n') 337 | LIB_file.write(' }\n') 338 | LIB_file.write(' }\n') 339 | 340 | LIB_file.write(' pin(ce_in){\n') 341 | LIB_file.write(' direction : input;\n') 342 | LIB_file.write(' capacitance : %.3f;\n' % (min_driver_in_cap)) 343 | LIB_file.write(' timing() {\n') 344 | LIB_file.write(' related_pin : clk;\n') 345 | LIB_file.write(' timing_type : setup_rising ;\n') 346 | LIB_file.write(' rise_constraint(%s_constraint_template) {\n' % name) 347 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 348 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 349 | LIB_file.write(' values ( \\\n') 350 | LIB_file.write(' "%.3f, %.3f", \\\n' % (tsetup, tsetup)) 351 | LIB_file.write(' "%.3f, %.3f" \\\n' % (tsetup, tsetup)) 352 | LIB_file.write(' )\n') 353 | LIB_file.write(' }\n') 354 | LIB_file.write(' fall_constraint(%s_constraint_template) {\n' % name) 355 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 356 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 357 | LIB_file.write(' values ( \\\n') 358 | LIB_file.write(' "%.3f, %.3f", \\\n' % (tsetup, tsetup)) 359 | LIB_file.write(' "%.3f, %.3f" \\\n' % (tsetup, tsetup)) 360 | LIB_file.write(' )\n') 361 | LIB_file.write(' }\n') 362 | LIB_file.write(' } \n') 363 | LIB_file.write(' timing() {\n') 364 | LIB_file.write(' related_pin : clk;\n') 365 | LIB_file.write(' timing_type : hold_rising ;\n') 366 | LIB_file.write(' rise_constraint(%s_constraint_template) {\n' % name) 367 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 368 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 369 | LIB_file.write(' values ( \\\n') 370 | LIB_file.write(' "%.3f, %.3f", \\\n' % (thold, thold)) 371 | LIB_file.write(' "%.3f, %.3f" \\\n' % (thold, thold)) 372 | LIB_file.write(' )\n') 373 | LIB_file.write(' }\n') 374 | LIB_file.write(' fall_constraint(%s_constraint_template) {\n' % name) 375 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 376 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 377 | LIB_file.write(' values ( \\\n') 378 | LIB_file.write(' "%.3f, %.3f", \\\n' % (thold, thold)) 379 | LIB_file.write(' "%.3f, %.3f" \\\n' % (thold, thold)) 380 | LIB_file.write(' )\n') 381 | LIB_file.write(' }\n') 382 | LIB_file.write(' }\n') 383 | LIB_file.write(' internal_power(){\n') 384 | LIB_file.write(' rise_power(%s_energy_template_sigslew) {\n' % name) 385 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 386 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 387 | LIB_file.write(' }\n') 388 | LIB_file.write(' fall_power(%s_energy_template_sigslew) {\n' % name) 389 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 390 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 391 | LIB_file.write(' }\n') 392 | LIB_file.write(' }\n') 393 | LIB_file.write(' }\n') 394 | 395 | for i in range(int(num_rwport)) : 396 | LIB_file.write(' bus(addr_in) {\n') 397 | LIB_file.write(' bus_type : %s_ADDRESS;\n' % name) 398 | LIB_file.write(' direction : input;\n') 399 | LIB_file.write(' capacitance : %.3f;\n' % (min_driver_in_cap)) 400 | LIB_file.write(' timing() {\n') 401 | LIB_file.write(' related_pin : clk;\n') 402 | LIB_file.write(' timing_type : setup_rising ;\n') 403 | LIB_file.write(' rise_constraint(%s_constraint_template) {\n' % name) 404 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 405 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 406 | LIB_file.write(' values ( \\\n') 407 | LIB_file.write(' "%.3f, %.3f", \\\n' % (tsetup, tsetup)) 408 | LIB_file.write(' "%.3f, %.3f" \\\n' % (tsetup, tsetup)) 409 | LIB_file.write(' )\n') 410 | LIB_file.write(' }\n') 411 | LIB_file.write(' fall_constraint(%s_constraint_template) {\n' % name) 412 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 413 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 414 | LIB_file.write(' values ( \\\n') 415 | LIB_file.write(' "%.3f, %.3f", \\\n' % (tsetup, tsetup)) 416 | LIB_file.write(' "%.3f, %.3f" \\\n' % (tsetup, tsetup)) 417 | LIB_file.write(' )\n') 418 | LIB_file.write(' }\n') 419 | LIB_file.write(' } \n') 420 | LIB_file.write(' timing() {\n') 421 | LIB_file.write(' related_pin : clk;\n') 422 | LIB_file.write(' timing_type : hold_rising ;\n') 423 | LIB_file.write(' rise_constraint(%s_constraint_template) {\n' % name) 424 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 425 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 426 | LIB_file.write(' values ( \\\n') 427 | LIB_file.write(' "%.3f, %.3f", \\\n' % (thold, thold)) 428 | LIB_file.write(' "%.3f, %.3f" \\\n' % (thold, thold)) 429 | LIB_file.write(' )\n') 430 | LIB_file.write(' }\n') 431 | LIB_file.write(' fall_constraint(%s_constraint_template) {\n' % name) 432 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 433 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 434 | LIB_file.write(' values ( \\\n') 435 | LIB_file.write(' "%.3f, %.3f", \\\n' % (thold, thold)) 436 | LIB_file.write(' "%.3f, %.3f" \\\n' % (thold, thold)) 437 | LIB_file.write(' )\n') 438 | LIB_file.write(' }\n') 439 | LIB_file.write(' }\n') 440 | LIB_file.write(' internal_power(){\n') 441 | LIB_file.write(' rise_power(%s_energy_template_sigslew) {\n' % name) 442 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 443 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 444 | LIB_file.write(' }\n') 445 | LIB_file.write(' fall_power(%s_energy_template_sigslew) {\n' % name) 446 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 447 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 448 | LIB_file.write(' }\n') 449 | LIB_file.write(' }\n') 450 | LIB_file.write(' }\n') 451 | 452 | for i in range(int(num_rwport)) : 453 | LIB_file.write(' bus(wd_in) {\n') 454 | LIB_file.write(' bus_type : %s_DATA;\n' % name) 455 | LIB_file.write(' memory_write() {\n') 456 | LIB_file.write(' address : addr_in;\n') 457 | LIB_file.write(' clocked_on : "clk";\n') 458 | LIB_file.write(' }\n') 459 | LIB_file.write(' direction : input;\n') 460 | LIB_file.write(' capacitance : %.3f;\n' % (min_driver_in_cap)) 461 | LIB_file.write(' timing() {\n') 462 | LIB_file.write(' related_pin : clk;\n') 463 | LIB_file.write(' timing_type : setup_rising ;\n') 464 | LIB_file.write(' rise_constraint(%s_constraint_template) {\n' % name) 465 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 466 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 467 | LIB_file.write(' values ( \\\n') 468 | LIB_file.write(' "%.3f, %.3f", \\\n' % (tsetup, tsetup)) 469 | LIB_file.write(' "%.3f, %.3f" \\\n' % (tsetup, tsetup)) 470 | LIB_file.write(' )\n') 471 | LIB_file.write(' }\n') 472 | LIB_file.write(' fall_constraint(%s_constraint_template) {\n' % name) 473 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 474 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 475 | LIB_file.write(' values ( \\\n') 476 | LIB_file.write(' "%.3f, %.3f", \\\n' % (tsetup, tsetup)) 477 | LIB_file.write(' "%.3f, %.3f" \\\n' % (tsetup, tsetup)) 478 | LIB_file.write(' )\n') 479 | LIB_file.write(' }\n') 480 | LIB_file.write(' } \n') 481 | LIB_file.write(' timing() {\n') 482 | LIB_file.write(' related_pin : clk;\n') 483 | LIB_file.write(' timing_type : hold_rising ;\n') 484 | LIB_file.write(' rise_constraint(%s_constraint_template) {\n' % name) 485 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 486 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 487 | LIB_file.write(' values ( \\\n') 488 | LIB_file.write(' "%.3f, %.3f", \\\n' % (thold, thold)) 489 | LIB_file.write(' "%.3f, %.3f" \\\n' % (thold, thold)) 490 | LIB_file.write(' )\n') 491 | LIB_file.write(' }\n') 492 | LIB_file.write(' fall_constraint(%s_constraint_template) {\n' % name) 493 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 494 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 495 | LIB_file.write(' values ( \\\n') 496 | LIB_file.write(' "%.3f, %.3f", \\\n' % (thold, thold)) 497 | LIB_file.write(' "%.3f, %.3f" \\\n' % (thold, thold)) 498 | LIB_file.write(' )\n') 499 | LIB_file.write(' }\n') 500 | LIB_file.write(' }\n') 501 | LIB_file.write(' internal_power(){\n') 502 | LIB_file.write(' when : "(! (we_in) )";\n') 503 | LIB_file.write(' rise_power(%s_energy_template_sigslew) {\n' % name) 504 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 505 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 506 | LIB_file.write(' }\n') 507 | LIB_file.write(' fall_power(%s_energy_template_sigslew) {\n' % name) 508 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 509 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 510 | LIB_file.write(' }\n') 511 | LIB_file.write(' }\n') 512 | LIB_file.write(' internal_power(){\n') 513 | LIB_file.write(' when : "(we_in)";\n') 514 | LIB_file.write(' rise_power(%s_energy_template_sigslew) {\n' % name) 515 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 516 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 517 | LIB_file.write(' }\n') 518 | LIB_file.write(' fall_power(%s_energy_template_sigslew) {\n' % name) 519 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 520 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 521 | LIB_file.write(' }\n') 522 | LIB_file.write(' }\n') 523 | LIB_file.write(' }\n') 524 | 525 | for i in range(int(num_rwport)) : 526 | LIB_file.write(' bus(w_mask_in) {\n') 527 | LIB_file.write(' bus_type : %s_DATA;\n' % name) 528 | LIB_file.write(' memory_write() {\n') 529 | LIB_file.write(' address : addr_in;\n') 530 | LIB_file.write(' clocked_on : "clk";\n') 531 | LIB_file.write(' }\n') 532 | LIB_file.write(' direction : input;\n') 533 | LIB_file.write(' capacitance : %.3f;\n' % (min_driver_in_cap)) 534 | LIB_file.write(' timing() {\n') 535 | LIB_file.write(' related_pin : clk;\n') 536 | LIB_file.write(' timing_type : setup_rising ;\n') 537 | LIB_file.write(' rise_constraint(%s_constraint_template) {\n' % name) 538 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 539 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 540 | LIB_file.write(' values ( \\\n') 541 | LIB_file.write(' "%.3f, %.3f", \\\n' % (tsetup, tsetup)) 542 | LIB_file.write(' "%.3f, %.3f" \\\n' % (tsetup, tsetup)) 543 | LIB_file.write(' )\n') 544 | LIB_file.write(' }\n') 545 | LIB_file.write(' fall_constraint(%s_constraint_template) {\n' % name) 546 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 547 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 548 | LIB_file.write(' values ( \\\n') 549 | LIB_file.write(' "%.3f, %.3f", \\\n' % (tsetup, tsetup)) 550 | LIB_file.write(' "%.3f, %.3f" \\\n' % (tsetup, tsetup)) 551 | LIB_file.write(' )\n') 552 | LIB_file.write(' }\n') 553 | LIB_file.write(' } \n') 554 | LIB_file.write(' timing() {\n') 555 | LIB_file.write(' related_pin : clk;\n') 556 | LIB_file.write(' timing_type : hold_rising ;\n') 557 | LIB_file.write(' rise_constraint(%s_constraint_template) {\n' % name) 558 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 559 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 560 | LIB_file.write(' values ( \\\n') 561 | LIB_file.write(' "%.3f, %.3f", \\\n' % (thold, thold)) 562 | LIB_file.write(' "%.3f, %.3f" \\\n' % (thold, thold)) 563 | LIB_file.write(' )\n') 564 | LIB_file.write(' }\n') 565 | LIB_file.write(' fall_constraint(%s_constraint_template) {\n' % name) 566 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 567 | LIB_file.write(' index_2 ("%s");\n' % slew_indicies) 568 | LIB_file.write(' values ( \\\n') 569 | LIB_file.write(' "%.3f, %.3f", \\\n' % (thold, thold)) 570 | LIB_file.write(' "%.3f, %.3f" \\\n' % (thold, thold)) 571 | LIB_file.write(' )\n') 572 | LIB_file.write(' }\n') 573 | LIB_file.write(' }\n') 574 | LIB_file.write(' internal_power(){\n') 575 | LIB_file.write(' when : "(! (we_in) )";\n') 576 | LIB_file.write(' rise_power(%s_energy_template_sigslew) {\n' % name) 577 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 578 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 579 | LIB_file.write(' }\n') 580 | LIB_file.write(' fall_power(%s_energy_template_sigslew) {\n' % name) 581 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 582 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 583 | LIB_file.write(' }\n') 584 | LIB_file.write(' }\n') 585 | LIB_file.write(' internal_power(){\n') 586 | LIB_file.write(' when : "(we_in)";\n') 587 | LIB_file.write(' rise_power(%s_energy_template_sigslew) {\n' % name) 588 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 589 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 590 | LIB_file.write(' }\n') 591 | LIB_file.write(' fall_power(%s_energy_template_sigslew) {\n' % name) 592 | LIB_file.write(' index_1 ("%s");\n' % slew_indicies) 593 | LIB_file.write(' values ("%.3f, %.3f")\n' % (pindynamic, pindynamic)) 594 | LIB_file.write(' }\n') 595 | LIB_file.write(' }\n') 596 | LIB_file.write(' }\n') 597 | 598 | LIB_file.write(' cell_leakage_power : %.3f;\n' % (leakage)) 599 | LIB_file.write('}\n') 600 | 601 | LIB_file.write('\n') 602 | LIB_file.write('}\n') 603 | 604 | LIB_file.close() 605 | 606 | --------------------------------------------------------------------------------